public async Task <QueryResultList <T> > Handle(GetListQuery <T> request, CancellationToken cancellationToken) { // 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. string dataAccessTypeString = string.Format("Get{0}List", typeof(T).Name); var dataAccessType = (DataAccessType)Enum.Parse(typeof(DataAccessType), dataAccessTypeString); AuditLogEvent auditLogNotification = new AuditLogEvent(_currentUser.UserName, Guid.Empty, dataAccessType); await _mediator.Publish(auditLogNotification); // Call the GetList method to get a list of the requested aggregate type, using the provided paging parameters and filter var result = await GetList(request.Paging); if (result.ResultType == ResultType.OkForQuery) { // Return the result list identifier return(new QueryResultList <T>(result.Content)); } else { if (result.ResultType == ResultType.AccessDenied) { // Drop an AuditLogEvent onto the mediator to indicate that this action was denied AuditLogEvent accessDeniedAuditEntry = new AuditLogEvent(_currentUser.UserName, Guid.Empty, dataAccessType, true); await _mediator.Publish(accessDeniedAuditEntry); } // Query returned a non-success result, so return the result, and the errors return(new QueryResultList <T>(result.ResultType)); } }
public IActionResult Get([FromQuery] AuditLogEvent parameters = null) { try { var model = repository.Get(); return(Ok(model)); } catch (Exception ex) { logger.LogError(ex.GetExceptionMessages()); return(StatusCode(StatusCodes.Status500InternalServerError, Constants.ErrorMessages.FetchError)); } }
/// <summary> /// Handle a DELETE command which specifies that an aggregate should be marked as deleted /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task <CommandResult <Guid> > Handle(DeleteCommand <T> request, CancellationToken cancellationToken) { // Validate input if (request.EventStreamId == Guid.Empty) { return(new CommandResult <Guid>(ResultType.BadRequest)); } if (await GetSpecificAggregateRoot(request.EventStreamId) == 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.AuthoriseDelete(request.EventStreamId); 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)); } // Create a domain event record for this aggregate, and this entity (This is an UPDATE for the aggregate as we're not DELETING the record physically) string eventData = new JObject() { new JProperty("Status", EntityStatus.Cancelled.ToString()) }.ToString(); DomainEventType eventType = (DomainEventType)Enum.Parse(typeof(DomainEventType), ($"Modify{typeof(T).Name}Event")); DomainEvent domainEvent = new DomainEvent(eventData, _currentUser.UserName, eventType, request.EventStreamId); // 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 we are not physically deleting the record, rather we're changing its status. UpdatedEvent <T> eventNotification = new UpdatedEvent <T>(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. // We are going to audit this as a DELETE even though it's enacted as a modify, for clarity AuditLogEvent auditLogNotification = new AuditLogEvent((DomainEventType)Enum.Parse(typeof(DomainEventType), ($"Delete{typeof(T).Name}Event")), _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>()); }
/// <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)); } }
/// <summary> /// Handle CREATE commands /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task <CommandResult <Guid> > Handle(CreateCommand <T> request, CancellationToken cancellationToken) { // Validate input if (string.IsNullOrWhiteSpace(request.EventData)) { return(new CommandResult <Guid>(ResultType.BadRequest)); } // Call extension point to get the action authorised for the user in question, and the specific resource var authResult = await this.AuthoriseCreate(request.EventData); 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 { // This is a Create event (and thus starts a new event stream) so we need to create the Event Stream Id Guid eventStreamId = Guid.NewGuid(); // Save the domain event record to the database DomainEventType eventType = (DomainEventType)Enum.Parse(typeof(DomainEventType), string.Format("Create{0}Event", typeof(T).Name)); DomainEvent domainEvent = new DomainEvent(request.EventData, _currentUser.UserName, eventType, eventStreamId); _dc.DomainEvents.Add(domainEvent); await _dc.SaveChangesAsync(); // Now publish events to signal the domain model to be updated, and to update the audit log CreatedEvent <T> eventNotification = new CreatedEvent <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(eventNotification.DomainEventType, _currentUser.UserName, domainEvent.EventStreamId, domainEvent.Id); await _mediator.Publish(auditLogNotification); // If the response was not 201 CREATED (or it is, and we have errors) then we need to ensure that we pass the correct response code back, along with any "error" messages //return new CommandResult<Guid>(eventStreamId); return(new CommandResult <Guid>(authResult.ResultType, eventStreamId, authResult.Errors)); } }
/// <summary> /// Handle a PATCh command which specifies updates on a child entity of the aggregate root /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task <CommandResult <Guid> > Handle(ModifyChildCommand <T> request, CancellationToken cancellationToken) { // Validate input if (request.ParentEventStreamId == Guid.Empty || request.ChildEntityId < 1 || request.ChildEntityType == null || string.IsNullOrWhiteSpace(request.EventData)) { return(new CommandResult <Guid>(ResultType.BadRequest)); } T aggregateRoot = await GetSpecificAggregateRoot(request.ParentEventStreamId); // find the child collection var childCollectionProperty = aggregateRoot?.GetType().GetProperties().Where(p => p.PropertyType.GenericTypeArguments.Contains(request.ChildEntityType)).First(); IEnumerable childCollection = (IEnumerable)childCollectionProperty?.GetMethod.Invoke(aggregateRoot, null); int childCollectionCount = childCollectionProperty == null || childCollection == null ? 0 : (int)childCollectionProperty.PropertyType.GetProperty("Count").GetValue(childCollection); bool childIdFound = false; if (childCollection != null) { foreach (dynamic child in childCollection) { if (child.Id == request.ChildEntityId) { childIdFound = true; break; } } } // TODO: Return nothing found if the childentityId is not in the collection if (aggregateRoot == null || childCollection == null || childCollectionCount == 0 || childIdFound == false) { return(new CommandResult <Guid>(ResultType.NothingFound)); } // Call extension point to get the action authorised for the user in question, and the specific resource. // This is a modify on a child entity, we're going to rely on the permission for modification of the parent entity for this. 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)); } // Create a domain event record for this aggregate, and this entity (This is an UPDATE for the aggregate, since it's updating child data owned by this aggregate root) DomainEventType eventType = (DomainEventType)Enum.Parse(typeof(DomainEventType), ($"Modify{request.ChildEntityType.Name}Event")); DomainEvent domainEvent = new DomainEvent(request.EventData, _currentUser.UserName, eventType, request.ParentEventStreamId, request.ChildEntityId); // 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 we are not physically deleting the record, rather we're changing its status. 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. // We are going to audit this as a DELETE even though it's enacted as a modify, for clarity AuditLogEvent auditLogNotification = new AuditLogEvent((DomainEventType)Enum.Parse(typeof(DomainEventType), ($"Modify{request.ChildEntityType.Name}Event")), _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>()); }