private async Task <DomainId[]> FindIdAsync(BulkTask task) { var id = task.CommandJob.Id; if (id != null) { return(new[] { id.Value }); } if (task.CommandJob.Query != null) { task.CommandJob.Query.Take = task.CommandJob.ExpectedCount; var existing = await contentQuery.QueryAsync(contextProvider.Context, task.Schema, Q.Empty.WithJsonQuery(task.CommandJob.Query)); if (existing.Total > task.CommandJob.ExpectedCount) { throw new DomainException(T.Get("contents.bulkInsertQueryNotUnique")); } if (existing.Count == 0 && task.CommandJob.Type == BulkUpdateContentType.Upsert) { return(new[] { DomainId.NewGuid() }); } return(existing.Select(x => x.Id).ToArray()); } if (task.CommandJob.Type == BulkUpdateContentType.Create || task.CommandJob.Type == BulkUpdateContentType.Upsert) { return(new[] { DomainId.NewGuid() }); } return(Array.Empty <DomainId>()); }
private AssetCommand CreateCommandCore(BulkTask task) { var job = task.CommandJob; switch (job.Type) { case BulkUpdateAssetType.Annotate: { var command = new AnnotateAsset(); EnrichAndCheckPermission(task, command, Permissions.AppAssetsUpdate); return(command); } case BulkUpdateAssetType.Move: { var command = new MoveAsset(); EnrichAndCheckPermission(task, command, Permissions.AppAssetsUpdate); return(command); } case BulkUpdateAssetType.Delete: { var command = new DeleteAsset(); EnrichAndCheckPermission(task, command, Permissions.AppAssetsDelete); return(command); } default: throw new NotSupportedException(); } }
private void EnrichAndCheckPermission <T>(BulkTask task, T command, string permissionId) where T : AssetCommand { SimpleMapper.Map(task.Command, command); SimpleMapper.Map(task.CommandJob, command); if (!contextProvider.Context.Allows(permissionId)) { throw new DomainForbiddenException("Forbidden"); } command.ExpectedVersion = task.Command.ExpectedVersion; }
private async Task <IEnumerable <BulkTaskCommand> > CreateCommandsAsync(BulkTask task) { var commands = new List <BulkTaskCommand>(); try { var resolvedIds = await FindIdAsync(task); if (resolvedIds.Length == 0) { throw new DomainObjectNotFoundException("undefined"); } foreach (var id in resolvedIds) { try { var command = await CreateCommandAsync(task); command.ContentId = id; commands.Add(new BulkTaskCommand(task, id, command)); } catch (Exception ex) { task.Results.Add(new BulkUpdateResultItem { Id = id, JobIndex = task.JobIndex, Exception = ex }); } } } catch (Exception ex) { task.Results.Add(new BulkUpdateResultItem { JobIndex = task.JobIndex, Exception = ex }); } return(commands); }
private BulkTaskCommand?CreateCommand(BulkTask task) { var id = task.CommandJob.Id; try { var command = CreateCommandCore(task); command.AssetId = id; return(new BulkTaskCommand(task, id, command)); } catch (Exception ex) { task.Results.Add(new BulkUpdateResultItem(id, task.JobIndex, ex)); return(null); } }
private async Task EnrichAndCheckPermissionAsync <T>(BulkTask task, T command, string permissionId) where T : ContentCommand { SimpleMapper.Map(task.Command, command); SimpleMapper.Map(task.CommandJob, command); if (!string.IsNullOrWhiteSpace(task.CommandJob.Schema)) { var schema = await contentQuery.GetSchemaOrThrowAsync(contextProvider.Context, task.Schema); command.SchemaId = schema.NamedId(); } if (!contextProvider.Context.Allows(permissionId, command.SchemaId.Name)) { throw new DomainForbiddenException("Forbidden"); } command.ExpectedVersion = task.Command.ExpectedVersion; }
private async Task <IEnumerable <BulkTaskCommand> > CreateCommandsAsync(BulkTask task) { var commands = new List <BulkTaskCommand>(); try { var resolvedIds = await FindIdAsync(task); if (resolvedIds.Length == 0) { throw new DomainObjectNotFoundException("undefined"); } foreach (var id in resolvedIds) { try { var command = await CreateCommandAsync(task); command.ContentId = id; commands.Add(new BulkTaskCommand(task, id, command)); } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "BulkContent") .WriteProperty("status", "Failed") .WriteProperty("jobIndex", task.JobIndex) .WriteProperty("jobType", task.CommandJob.Type.ToString())); task.Results.Add(new BulkUpdateResultItem(id, task.JobIndex, ex)); } } } catch (Exception ex) { task.Results.Add(new BulkUpdateResultItem(null, task.JobIndex, ex)); } return(commands); }
private async Task EnrichAsync <TCommand>(DomainId id, BulkTask task, TCommand command, string permissionId) where TCommand : ContentCommand { SimpleMapper.Map(task.Command, command); command.ContentId = id; if (!string.IsNullOrWhiteSpace(task.Job.Schema)) { var schema = await contentQuery.GetSchemaOrThrowAsync(task.Context, task.Schema); command.SchemaId = schema.NamedId(); } if (!task.Context.Allows(permissionId, command.SchemaId.Name)) { throw new DomainForbiddenException("Forbidden"); } command.ExpectedVersion = task.Command.ExpectedVersion; }
private async Task <IEnumerable <BulkTaskCommand> > CreateCommandsAsync(BulkTask task) { var commands = new List <BulkTaskCommand>(); try { var resolvedIds = await FindIdAsync(task); if (resolvedIds.Length == 0) { throw new DomainObjectNotFoundException("undefined"); } foreach (var id in resolvedIds) { try { var command = await CreateCommandAsync(task); command.ContentId = id; commands.Add(new BulkTaskCommand(task, id, command)); } catch (Exception ex) { log.LogError(ex, "Failed to execute content bulk job with index {index} of type {type}.", task.JobIndex, task.CommandJob.Type); task.Results.Add(new BulkUpdateResultItem(id, task.JobIndex, ex)); } } } catch (Exception ex) { task.Results.Add(new BulkUpdateResultItem(null, task.JobIndex, ex)); } return(commands); }
private BulkTaskCommand?CreateCommand(BulkTask task) { var id = task.CommandJob.Id; try { var command = CreateCommandCore(task); command.AssetId = id; return(new BulkTaskCommand(task, id, command)); } catch (Exception ex) { log.LogError(ex, "Faield to execute asset bulk job with index {index} of type {type}.", task.JobIndex, task.CommandJob.Type); task.Results.Add(new BulkUpdateResultItem(id, task.JobIndex, ex)); return(null); } }
private BulkTaskCommand?CreateCommand(BulkTask task) { var id = task.CommandJob.Id; try { var command = CreateCommandCore(task); command.AssetId = id; return(new BulkTaskCommand(task, id, command)); } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "BulkContent") .WriteProperty("status", "Failed") .WriteProperty("jobIndex", task.JobIndex) .WriteProperty("jobType", task.CommandJob.Type.ToString())); task.Results.Add(new BulkUpdateResultItem(id, task.JobIndex, ex)); return(null); } }
public async Task HandleAsync(CommandContext context, NextDelegate next) { if (context.Command is BulkUpdateContents bulkUpdates) { if (bulkUpdates.Jobs?.Length > 0) { var executionOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }; var createCommandsBlock = new TransformManyBlock <BulkTask, BulkTaskCommand>(async task => { try { return(await CreateCommandsAsync(task)); } catch (OperationCanceledException ex) { // Dataflow swallows operation cancelled exception. throw new AggregateException(ex); } }, executionOptions); var executeCommandBlock = new ActionBlock <BulkTaskCommand>(async command => { try { await ExecuteCommandAsync(command); } catch (OperationCanceledException ex) { // Dataflow swallows operation cancelled exception. throw new AggregateException(ex); } }, executionOptions); createCommandsBlock.BidirectionalLinkTo(executeCommandBlock); contextProvider.Context.Change(b => b .WithoutContentEnrichment() .WithoutCleanup() .WithUnpublished(true) .WithoutTotal()); var requestedSchema = bulkUpdates.SchemaId.Name; var results = new ConcurrentBag <BulkUpdateResultItem>(); for (var i = 0; i < bulkUpdates.Jobs.Length; i++) { var task = new BulkTask( context.CommandBus, requestedSchema, i, bulkUpdates.Jobs[i], bulkUpdates, results); if (!await createCommandsBlock.SendAsync(task)) { break; } } createCommandsBlock.Complete(); await executeCommandBlock.Completion; context.Complete(new BulkUpdateResult(results)); } else { context.Complete(new BulkUpdateResult()); } } else { await next(context); } }
private sealed record BulkTaskCommand(BulkTask Task, DomainId Id, ICommand Command) { }
private async Task <ContentCommand> CreateCommandAsync(BulkTask task) { var job = task.CommandJob; switch (job.Type) { case BulkUpdateContentType.Create: { var command = new CreateContent(); await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsCreate); return(command); } case BulkUpdateContentType.Update: { var command = new UpdateContent(); await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsUpdateOwn); return(command); } case BulkUpdateContentType.Upsert: { var command = new UpsertContent(); await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsUpsert); return(command); } case BulkUpdateContentType.Patch: { var command = new PatchContent(); await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsUpdateOwn); return(command); } case BulkUpdateContentType.Validate: { var command = new ValidateContent(); await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsReadOwn); return(command); } case BulkUpdateContentType.ChangeStatus: { var command = new ChangeContentStatus { Status = job.Status ?? Status.Draft }; await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsChangeStatusOwn); return(command); } case BulkUpdateContentType.Delete: { var command = new DeleteContent(); await EnrichAndCheckPermissionAsync(task, command, Permissions.AppContentsDeleteOwn); return(command); } default: throw new NotSupportedException(); } }
public async Task HandleAsync(CommandContext context, NextDelegate next) { if (context.Command is BulkUpdateAssets bulkUpdates) { if (bulkUpdates.Jobs?.Length > 0) { var executionOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }; var createCommandsBlock = new TransformBlock <BulkTask, BulkTaskCommand?>(task => { return(CreateCommand(task)); }, executionOptions); var executeCommandBlock = new ActionBlock <BulkTaskCommand?>(async command => { if (command != null) { await ExecuteCommandAsync(command); } }, executionOptions); createCommandsBlock.LinkTo(executeCommandBlock, new DataflowLinkOptions { PropagateCompletion = true }); contextProvider.Context.Change(b => b .WithoutAssetEnrichment() .WithoutCleanup() .WithUnpublished(true) .WithoutTotal()); var results = new ConcurrentBag <BulkUpdateResultItem>(); for (var i = 0; i < bulkUpdates.Jobs.Length; i++) { var task = new BulkTask( context.CommandBus, i, bulkUpdates.Jobs[i], bulkUpdates, results); await createCommandsBlock.SendAsync(task); } createCommandsBlock.Complete(); await executeCommandBlock.Completion; context.Complete(new BulkUpdateResult(results)); } else { context.Complete(new BulkUpdateResult()); } } else { await next(context); } }
private async Task <ICommand> CreateCommandAsync(DomainId id, BulkTask task) { var job = task.Job; switch (job.Type) { case BulkUpdateType.Create: { var command = new CreateContent { Data = job.Data ! }; await EnrichAsync(id, task, command, Permissions.AppContentsCreate); return(command); } case BulkUpdateType.Update: { var command = new UpdateContent { Data = job.Data ! }; await EnrichAsync(id, task, command, Permissions.AppContentsUpdateOwn); return(command); } case BulkUpdateType.Upsert: { var command = new UpsertContent { Data = job.Data ! }; await EnrichAsync(id, task, command, Permissions.AppContentsUpsert); return(command); } case BulkUpdateType.Patch: { var command = new PatchContent { Data = job.Data ! }; await EnrichAsync(id, task, command, Permissions.AppContentsUpdateOwn); return(command); } case BulkUpdateType.Validate: { var command = new ValidateContent(); await EnrichAsync(id, task, command, Permissions.AppContentsReadOwn); return(command); } case BulkUpdateType.ChangeStatus: { var command = new ChangeContentStatus { Status = job.Status, DueTime = job.DueTime }; await EnrichAsync(id, task, command, Permissions.AppContentsUpdateOwn); return(command); } case BulkUpdateType.Delete: { var command = new DeleteContent(); await EnrichAsync(id, task, command, Permissions.AppContentsDeleteOwn); return(command); } default: throw new NotSupportedException(); } }