Merge pull request #1229 from GreenShadeZhang/custom_tenantid_source

Add ITenantAccessor interface to provide custom ability for acquiring tenantid
This commit is contained in:
Sipke Schoorstra 2021-07-09 12:49:24 +02:00 committed by GitHub
commit ee4536f920
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 290 additions and 38 deletions

View file

@ -267,8 +267,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{3BF2C5
ProjectSection(SolutionItems) = preProject
.github\workflows\publish-latest-dashboard-and-server-docker.yml = .github\workflows\publish-latest-dashboard-and-server-docker.yml
.github\workflows\publish-latest-dashboard-docker.yml = .github\workflows\publish-latest-dashboard-docker.yml
.github\workflows\publish-latest-server-docker.yml = .github\workflows\publish-latest-server-docker.yml
.github\workflows\publish-latest-designer-npm.yml = .github\workflows\publish-latest-designer-npm.yml
.github\workflows\publish-latest-server-docker.yml = .github\workflows\publish-latest-server-docker.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.SignalApi", "src\samples\aspnet\Elsa.Samples.SignalApi\Elsa.Samples.SignalApi.csproj", "{3E2423CF-50E6-4D2B-8749-17B1EF540FE4}"
@ -325,6 +325,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Activities.Conductor",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aspnetcore", "aspnetcore", "{39DE1EE2-DF4F-44EB-9F5A-551BEADD6673}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.CustomTenantIdSource", "src\samples\aspnet\Elsa.Samples.CustomTenantIdSource\Elsa.Samples.CustomTenantIdSource.csproj", "{6E191628-927B-42FB-8805-D2977D8FC4E3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -751,6 +753,10 @@ Global
{4D4022DD-244B-47EE-95FA-7071EC687B24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D4022DD-244B-47EE-95FA-7071EC687B24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D4022DD-244B-47EE-95FA-7071EC687B24}.Release|Any CPU.Build.0 = Release|Any CPU
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E191628-927B-42FB-8805-D2977D8FC4E3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -893,6 +899,7 @@ Global
{84751112-C2B7-41D4-98F5-D87D7C2E1DAE} = {22E75696-6FE9-436A-9097-EE21C603F818}
{4D4022DD-244B-47EE-95FA-7071EC687B24} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180}
{39DE1EE2-DF4F-44EB-9F5A-551BEADD6673} = {4673732F-2853-47BD-91B8-C95C229D2C89}
{6E191628-927B-42FB-8805-D2977D8FC4E3} = {22E75696-6FE9-436A-9097-EE21C603F818}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}

View file

@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Services
{
public interface ITenantAccessor
{
Task<string?> GetTenantIdAsync(CancellationToken cancellationToken = default);
}
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -33,7 +33,7 @@ namespace Elsa.Activities.Signaling.Services
public async Task<IEnumerable<CollectedWorkflow>> TriggerSignalAsync(string signal, object? input = default, string? workflowInstanceId = default, string? correlationId = default, CancellationToken cancellationToken = default)
{
var normalizedSignal = signal.ToLowerInvariant();
return await _workflowLaunchpad.CollectAndExecuteWorkflowsAsync(new CollectWorkflowsContext(
nameof(SignalReceived),
new SignalReceivedBookmark { Signal = normalizedSignal, WorkflowInstanceId = workflowInstanceId },
@ -54,16 +54,16 @@ namespace Elsa.Activities.Signaling.Services
}
public async Task<IEnumerable<CollectedWorkflow>> DispatchSignalAsync(string signal, object? input = default, string? workflowInstanceId = default, string? correlationId = default, CancellationToken cancellationToken = default) =>
await _workflowLaunchpad.CollectAndDispatchWorkflowsAsync(new CollectWorkflowsContext(
nameof(SignalReceived),
new SignalReceivedBookmark { Signal = signal, WorkflowInstanceId = workflowInstanceId },
new SignalReceivedBookmark { Signal = signal },
correlationId,
workflowInstanceId,
default,
TenantId
),
new Signal(signal, input),
cancellationToken);
await _workflowLaunchpad.CollectAndDispatchWorkflowsAsync(new CollectWorkflowsContext(
nameof(SignalReceived),
new SignalReceivedBookmark { Signal = signal, WorkflowInstanceId = workflowInstanceId },
new SignalReceivedBookmark { Signal = signal },
correlationId,
workflowInstanceId,
default,
TenantId
),
new Signal(signal, input),
cancellationToken);
}
}

View file

@ -235,5 +235,11 @@ namespace Elsa
ElsaOptions.ConfigureServiceBusEndpoint = setup;
return this;
}
public ElsaOptionsBuilder AddCustomTenantAccessor<T>() where T : class, ITenantAccessor
{
Services.AddScoped<ITenantAccessor, T>();
return this;
}
}
}

View file

@ -87,6 +87,9 @@ namespace Microsoft.Extensions.DependencyInjection
services.Decorate<IWorkflowInstanceStore, EventPublishingWorkflowInstanceStore>();
services.Decorate<IWorkflowInstanceExecutor, LockingWorkflowInstanceExecutor>();
//TenantId default source
services.TryAddScoped<ITenantAccessor, DefaultTenantAccessor>();
return services;
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Services
{
public class DefaultTenantAccessor : ITenantAccessor
{
public Task<string?> GetTenantIdAsync(CancellationToken cancellationToken = default)
{
string? tenantId = default;
return Task.FromResult(tenantId);
}
}
}

View file

@ -0,0 +1,34 @@
using Elsa.Services;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Samples.CustomTenantIdSource
{
public class CustomTenantAccessor : ITenantAccessor
{
private readonly IHttpContextAccessor _accessor;
public CustomTenantAccessor(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public Task<string> GetTenantIdAsync(CancellationToken cancellationToken = default)
{
//You can customize the data
var httpContext = _accessor.HttpContext;
var tenantId = httpContext.Request.Headers["x-tenant"].ToString();
// Or you can get tenantid from claim
//var tenantId = httpContext.User.FindFirstValue("x-tenant");
return Task.FromResult(tenantId);
}
}
}

View file

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Elsa.Abstractions\Elsa.Abstractions.csproj" />
<ProjectReference Include="..\..\..\core\Elsa\Elsa.csproj" />
<ProjectReference Include="..\..\..\persistence\Elsa.Persistence.EntityFramework\Elsa.Persistence.EntityFramework.Core\Elsa.Persistence.EntityFramework.Core.csproj" />
<ProjectReference Include="..\..\..\persistence\Elsa.Persistence.EntityFramework\Elsa.Persistence.EntityFramework.Sqlite\Elsa.Persistence.EntityFramework.Sqlite.csproj" />
<ProjectReference Include="..\..\..\server\Elsa.Server.Api\Elsa.Server.Api.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Elsa.Samples.CustomTenantIdSource
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View file

@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:2114",
"sslPort": 44301
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Elsa.Samples.CustomTenantIdSource": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View file

@ -0,0 +1,65 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Elsa.Persistence.EntityFramework.Core.Extensions;
using Elsa.Persistence.EntityFramework.Sqlite;
namespace Elsa.Samples.CustomTenantIdSource
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpContextAccessor();
services
.AddElsa(options => options
.UseEntityFrameworkPersistence(ef => ef.UseSqlite())
.AddConsoleActivities()
.AddActivitiesFrom<Startup>()
.AddCustomTenantAccessor<CustomTenantAccessor>()
);
services
.AddElsaSwagger()
.AddElsaApiEndpoints();
// Allow arbitrary client browser apps to access the API.
// In a production environment, make sure to allow only origins you trust.
services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("Content-Disposition")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Elsa"));
}
app.UseCors();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}

View file

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View file

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

View file

@ -1,11 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Serialization;
using Elsa.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
{
@ -17,11 +17,12 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
{
private readonly IWorkflowPublisher _workflowPublisher;
private readonly IContentSerializer _contentSerializer;
public Import(IWorkflowPublisher workflowPublisher, IContentSerializer contentSerializer)
private readonly ITenantAccessor _tenantAccessor;
public Import(IWorkflowPublisher workflowPublisher, IContentSerializer contentSerializer, ITenantAccessor tenantAccessor)
{
_workflowPublisher = workflowPublisher;
_contentSerializer = contentSerializer;
_tenantAccessor = tenantAccessor;
}
[HttpPost]
@ -32,15 +33,15 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
OperationId = "WorkflowDefinitions.Import",
Tags = new[] { "WorkflowDefinitions" })
]
public async Task<IActionResult> Handle(string workflowDefinitionId, [FromForm]IFormFile? file, CancellationToken cancellationToken)
public async Task<IActionResult> Handle(string workflowDefinitionId, [FromForm] IFormFile? file, CancellationToken cancellationToken)
{
if (file == null)
return BadRequest();
var json = await file.OpenReadStream().ReadStringToEndAsync(cancellationToken);
var workflowDefinition = await _workflowPublisher.GetDraftAsync(workflowDefinitionId, cancellationToken) ?? _workflowPublisher.New();
var postedModel = _contentSerializer.Deserialize<WorkflowDefinition>(json);
workflowDefinition.Activities = postedModel.Activities;
workflowDefinition.Connections = postedModel.Connections;
workflowDefinition.Description = postedModel.Description;
@ -53,6 +54,8 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
workflowDefinition.IsSingleton = postedModel.IsSingleton;
workflowDefinition.DeleteCompletedInstances = postedModel.DeleteCompletedInstances;
workflowDefinition.TenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
await _workflowPublisher.SaveDraftAsync(workflowDefinition, cancellationToken);
return Ok(workflowDefinition);
}

View file

@ -9,6 +9,7 @@ using Elsa.Persistence.Specifications.WorkflowDefinitions;
using Elsa.Serialization;
using Elsa.Server.Api.Models;
using Elsa.Server.Api.Swagger.Examples;
using Elsa.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
@ -25,12 +26,14 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
private readonly IWorkflowDefinitionStore _workflowDefinitionStore;
private readonly IContentSerializer _serializer;
private readonly IMapper _mapper;
private readonly ITenantAccessor _tenantAccessor;
public List(IWorkflowDefinitionStore workflowDefinitionStore, IContentSerializer serializer, IMapper mapper)
public List(IWorkflowDefinitionStore workflowDefinitionStore, IContentSerializer serializer, IMapper mapper, ITenantAccessor tenantAccessor)
{
_workflowDefinitionStore = workflowDefinitionStore;
_serializer = serializer;
_mapper = mapper;
_tenantAccessor = tenantAccessor;
}
[HttpGet]
@ -44,8 +47,9 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
]
public async Task<ActionResult<PagedList<WorkflowDefinitionSummaryModel>>> Handle(int? page = default, int? pageSize = default, VersionOptions? version = default, CancellationToken cancellationToken = default)
{
var tenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
version ??= VersionOptions.Latest;
var specification = new VersionOptionsSpecification(version.Value);
var specification = new VersionOptionsSpecification(version.Value).And(new TenantSpecification<WorkflowDefinition>(tenantId));
var totalCount = await _workflowDefinitionStore.CountAsync(specification, cancellationToken);
var paging = page == null || pageSize == null ? default : Paging.Page(page.Value, pageSize.Value);
var items = await _workflowDefinitionStore.FindManyAsync(specification, paging: paging, cancellationToken: cancellationToken);

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -19,10 +19,11 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
public partial class Save : ControllerBase
{
private readonly IWorkflowPublisher _workflowPublisher;
public Save(IWorkflowPublisher workflowPublisher)
private readonly ITenantAccessor _tenantAccessor;
public Save(IWorkflowPublisher workflowPublisher, ITenantAccessor tenantAccessor)
{
_workflowPublisher = workflowPublisher;
_tenantAccessor = tenantAccessor;
}
[HttpPost]
@ -35,7 +36,7 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
OperationId = "WorkflowDefinitions.Post",
Tags = new[] { "WorkflowDefinitions" })
]
public async Task<ActionResult<WorkflowDefinition>> Handle([FromBody]SaveWorkflowDefinitionRequest request, [FromRoute]ApiVersion apiVersion, CancellationToken cancellationToken)
public async Task<ActionResult<WorkflowDefinition>> Handle([FromBody] SaveWorkflowDefinitionRequest request, [FromRoute] ApiVersion apiVersion, CancellationToken cancellationToken)
{
var workflowDefinitionId = request.WorkflowDefinitionId;
var workflowDefinition = !string.IsNullOrWhiteSpace(workflowDefinitionId) ? await _workflowPublisher.GetDraftAsync(workflowDefinitionId, cancellationToken) : default;
@ -61,6 +62,8 @@ namespace Elsa.Server.Api.Endpoints.WorkflowDefinitions
workflowDefinition.Tag = request.Tag?.Trim();
workflowDefinition.Channel = request.Channel?.Trim();
workflowDefinition.TenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
if (request.Publish)
workflowDefinition = await _workflowPublisher.PublishAsync(workflowDefinition, cancellationToken);
else

View file

@ -1,10 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Server.Api.ActionFilters;
using Elsa.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Server.Api.Endpoints.Workflows
{
@ -15,10 +15,12 @@ namespace Elsa.Server.Api.Endpoints.Workflows
public class Dispatch : Controller
{
private readonly IWorkflowLaunchpad _workflowLaunchpad;
private readonly ITenantAccessor _tenantAccessor;
public Dispatch(IWorkflowLaunchpad workflowLaunchpad)
public Dispatch(IWorkflowLaunchpad workflowLaunchpad, ITenantAccessor tenantAccessor)
{
_workflowLaunchpad = workflowLaunchpad;
_tenantAccessor = tenantAccessor;
}
[HttpPost]
@ -33,11 +35,12 @@ namespace Elsa.Server.Api.Endpoints.Workflows
]
public async Task<IActionResult> Handle(string workflowDefinitionId, DispatchWorkflowDefinitionRequestModel request, CancellationToken cancellationToken = default)
{
var startableWorkflow = await _workflowLaunchpad.CollectStartableWorkflowAsync(workflowDefinitionId, request.ActivityId, request.CorrelationId, request.ContextId, default, cancellationToken);
var tenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
var startableWorkflow = await _workflowLaunchpad.CollectStartableWorkflowAsync(workflowDefinitionId, request.ActivityId, request.CorrelationId, request.ContextId, tenantId, cancellationToken);
if (startableWorkflow == null)
return NotFound();
var result = await _workflowLaunchpad.DispatchStartableWorkflowAsync(startableWorkflow, request.Input, cancellationToken);
return Ok(new DispatchWorkflowDefinitionResponseModel(result.WorkflowInstanceId, result.ActivityId));
}

View file

@ -1,10 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Server.Api.ActionFilters;
using Elsa.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Server.Api.Endpoints.Workflows
{
@ -15,10 +15,12 @@ namespace Elsa.Server.Api.Endpoints.Workflows
public class Execute : Controller
{
private readonly IWorkflowLaunchpad _workflowLaunchpad;
private readonly ITenantAccessor _tenantAccessor;
public Execute(IWorkflowLaunchpad workflowLaunchpad)
public Execute(IWorkflowLaunchpad workflowLaunchpad, ITenantAccessor tenantAccessor)
{
_workflowLaunchpad = workflowLaunchpad;
_tenantAccessor = tenantAccessor;
}
[HttpPost]
@ -33,7 +35,8 @@ namespace Elsa.Server.Api.Endpoints.Workflows
]
public async Task<IActionResult> Handle(string workflowDefinitionId, ExecuteWorkflowDefinitionRequestModel request, CancellationToken cancellationToken = default)
{
var startableWorkflow = await _workflowLaunchpad.CollectStartableWorkflowAsync(workflowDefinitionId, request.ActivityId, request.CorrelationId, request.ContextId, default, cancellationToken);
var tenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
var startableWorkflow = await _workflowLaunchpad.CollectStartableWorkflowAsync(workflowDefinitionId, request.ActivityId, request.CorrelationId, request.ContextId, tenantId, cancellationToken);
if (startableWorkflow == null)
return NotFound();

View file

@ -18,7 +18,12 @@ namespace Elsa.Server.Api.Endpoints.Workflows
public class Trigger : Controller
{
private readonly IWorkflowLaunchpad _workflowLaunchpad;
public Trigger(IWorkflowLaunchpad workflowLaunchpad) => _workflowLaunchpad = workflowLaunchpad;
private readonly ITenantAccessor _tenantAccessor;
public Trigger(IWorkflowLaunchpad workflowLaunchpad,ITenantAccessor tenantAccessor)
{
_workflowLaunchpad = workflowLaunchpad;
_tenantAccessor = tenantAccessor;
}
[HttpPost]
[ElsaJsonFormatter]
@ -32,7 +37,8 @@ namespace Elsa.Server.Api.Endpoints.Workflows
]
public async Task<IActionResult> Handle(TriggerWorkflowsRequestModel request, CancellationToken cancellationToken = default)
{
var context = new CollectWorkflowsContext(request.ActivityType, request.Bookmark, request.Trigger, request.CorrelationId, request.WorkflowInstanceId, request.ContextId);
var tenantId = await _tenantAccessor.GetTenantIdAsync(cancellationToken);
var context = new CollectWorkflowsContext(request.ActivityType, request.Bookmark, request.Trigger, request.CorrelationId, request.WorkflowInstanceId, request.ContextId, tenantId);
ICollection<TriggeredWorkflowModel> triggeredWorkflows;
if (request.Dispatch)