/// <summary> /// Handle a CREATE command which specifies that a new entity should be added to a child collection on an aggregate /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task <CommandResult <Guid> > Handle(CreateChildCommand <T> request, CancellationToken cancellationToken) { // Validate input if (string.IsNullOrWhiteSpace(request.EventData) || request.ParentEventStreamId == Guid.Empty) { return(new CommandResult <Guid>(ResultType.BadRequest)); } if (await GetSpecificAggregateRoot(request.ParentEventStreamId) == null) { return(new CommandResult <Guid>(ResultType.NothingFound)); } // Call extension point to get the action authorised for the user in question, and the specific resource var authResult = await this.AuthoriseModify(request.EventData, request.ParentEventStreamId, request.ChildEntityType); if (authResult.ResultType == ResultType.AccessDenied) { // TODO: Move the Access Denied audit log creation to here, from the child classes? return(new CommandResult <Guid>(authResult.ResultType)); } else { // Create a domain event record for this aggregate, and this new child entity (This is an UPDATE for the aggregate, recorded as a Create of the new child) DomainEventType eventType = (DomainEventType)Enum.Parse(typeof(DomainEventType), string.Format("Create{0}Event", request.ChildEntityType.Name)); DomainEvent domainEvent = new DomainEvent(request.EventData, _currentUser.UserName, eventType, request.ParentEventStreamId); // Save the domain event _dc.DomainEvents.Add(domainEvent); await _dc.SaveChangesAsync(); // Publish domain event notification - this is published as a MODIFY on the aggregate as this will direct it to the correct Aggregate's domain serivce. UpdatedEvent <T> eventNotification = new UpdatedEvent <T>(request.EventData, _currentUser.UserName, domainEvent.EventStreamId, domainEvent.Id); await _mediator.Publish(eventNotification); // Drop an AuditLogEvent onto the mediator, to dispatch a request to update the system audit log. Again, we're not going to wait for the outcome of this event. Just fire and forget. AuditLogEvent auditLogNotification = new AuditLogEvent(domainEvent.DomainEventType, _currentUser.UserName, domainEvent.EventStreamId, domainEvent.Id); await _mediator.Publish(auditLogNotification); // Return new command result, with the PARENT's event stream id. return(new CommandResult <Guid>(request.ParentEventStreamId)); } }
private IReadOnlyList <KernelCommand> SplitSubmission( KernelCommand originalCommand, string code, CreateChildCommand createCommand) { var commands = new List <KernelCommand>(); var nugetRestoreOnKernels = new HashSet <string>(); var hoistedCommandsIndex = 0; var tree = Parse(code, originalCommand.TargetKernelName); var nodes = tree.GetRoot().ChildNodes.ToArray(); var targetKernelName = originalCommand.TargetKernelName ?? KernelLanguage; var lastKernelUri = originalCommand.KernelUri; KernelNameDirectiveNode lastKernelNameNode = null; foreach (var node in nodes) { switch (node) { case DirectiveNode directiveNode: var parseResult = directiveNode.GetDirectiveParseResult(); if (parseResult.Errors.Any()) { if (directiveNode.IsUnknownActionDirective()) { commands.Add(createCommand(directiveNode, originalCommand, lastKernelNameNode)); } else { commands.Clear(); commands.Add( new AnonymousKernelCommand((kernelCommand, context) => { var message = string.Join(Environment.NewLine, parseResult.Errors .Select(e => e.ToString())); context.Fail(message: message); return(Task.CompletedTask); }, parent: originalCommand)); } break; } var directiveCommand = new DirectiveCommand( parseResult, originalCommand, directiveNode) { TargetKernelName = targetKernelName }; if (directiveNode is KernelNameDirectiveNode kernelNameNode) { targetKernelName = kernelNameNode.KernelName; lastKernelNameNode = kernelNameNode; } if (parseResult.CommandResult.Command.Name == "#r") { var value = parseResult.ValueForArgument <PackageReferenceOrFileInfo>("package"); if (value.Value is FileInfo) { AddHoistedCommand(createCommand(directiveNode, originalCommand, lastKernelNameNode)); } else { directiveCommand.KernelUri = lastKernelUri; directiveCommand.TargetKernelName = targetKernelName; AddHoistedCommand(directiveCommand); nugetRestoreOnKernels.Add(targetKernelName); } } else if (parseResult.CommandResult.Command.Name == "#i") { directiveCommand.KernelUri = lastKernelUri; directiveCommand.TargetKernelName = targetKernelName; AddHoistedCommand(directiveCommand); } else { commands.Add(directiveCommand); if (directiveNode is KernelNameDirectiveNode) { hoistedCommandsIndex = commands.Count; } } break; case LanguageNode languageNode: commands.Add(createCommand(languageNode, originalCommand, lastKernelNameNode)); break; default: throw new ArgumentOutOfRangeException(nameof(node)); } } foreach (var kernelName in nugetRestoreOnKernels) { var kernel = _kernel.FindKernel(kernelName); if (kernel?.SubmissionParser.GetDirectiveParser() is { } parser) { var restore = new DirectiveCommand( parser.Parse("#!nuget-restore"), originalCommand) { KernelUri = kernel.Uri, TargetKernelName = kernelName }; AddHoistedCommand(restore); } } if (NoSplitWasNeeded(out var originalSubmission)) { return(originalSubmission); } foreach (var command in commands) { command.Parent = originalCommand; } return(commands); void AddHoistedCommand(KernelCommand command) { commands.Insert(hoistedCommandsIndex++, command); } bool NoSplitWasNeeded(out IReadOnlyList <KernelCommand> splitSubmission) { if (commands.Count == 0) { splitSubmission = new[] { originalCommand }; return(true); } if (commands.Count == 1) { if (commands[0] is SubmitCode sc) { if (code.Equals(sc.Code, StringComparison.Ordinal)) { splitSubmission = new[] { originalCommand }; return(true); } } } splitSubmission = null; return(false); } }