Beispiel #1
0
        public async Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken)
        {
            var isStatusPropertyDirty = context.EditMode == EditMode.Create ||
                                        context.EditMode == EditMode.Update && context.IsPropertyDirty(x => x.Status);

            if (isStatusPropertyDirty)
            {
                switch (context.Entity.Status)
                {
                case InvoiceStatus.Sent:
                    await context.AddEventAsync(InvoiceSentEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);

                    break;

                case InvoiceStatus.Paid:
                    await context.AddEventAsync(InvoicePaidEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);

                    break;

                case InvoiceStatus.Cancelled:
                    await context.AddEventAsync(InvoiceCancelledEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);

                    break;
                }
            }
        }
Beispiel #2
0
        public Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Invoice> context, CancellationToken cancellationToken = default)
        {
            if (context.EditMode != EditMode.Update)
            {
                return(ValidationResultTask.Ok());
            }

            var statussesAllowedToModifyInvoiceContent = new[] { InvoiceStatus.Draft };

            if (!statussesAllowedToModifyInvoiceContent.Contains(context.Pristine.Status))
            {
                try
                {
                    context.Entity.InvoiceDate.Should().Be(context.Pristine.InvoiceDate);
                    context.Entity.CustomerId.Should().Be(context.Pristine.CustomerId);
                    context.Entity.InvoiceLines.Should().BeEquivalentTo(context.Pristine.InvoiceLines,
                                                                        options => options
                                                                        .Excluding(x => x.Invoice)
                                                                        //.Excluding(x => x.Item)
                                                                        .Excluding(x => x.Timestamp)
                                                                        );
                }
                catch (System.Exception ex)
                {
                    _logger.LogWarning(ex, "Not allowed to change invoice content in current status.");
                    return(ValidationResultTask.Invalid("Not allowed to change invoice content in current status."));
                }
            }

            return(ValidationResultTask.Ok());
        }
 public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken)
 {
     context.Entity.Fullname = $"{context.Entity.GivenName} {context.Entity.MiddleName} {context.Entity.FamilyName}"
                               .Replace("  ", " ")
                               .Trim();
     return(Task.CompletedTask);
 }
        public Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken)
        {
            context.Entity.Customer = null;
            // context.Entity.InvoiceLines?.ForEach(invoiceLine => invoiceLine.Item = null);

            return(Task.CompletedTask);
        }
 public Task ExecuteAsync(HookType type, IDomainEntityContext <FeatureFlagSettings> context, CancellationToken cancellationToken = default)
 {
     foreach (var featureFlag in context.Entity.Settings.FeatureFlags.Where(x => x.Id == default))
     {
         featureFlag.Id = Guid.NewGuid();
     }
     return(Task.CompletedTask);
 }
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Role> context, CancellationToken cancellationToken = default)
        {
            RuleFor(user => user.Name).NotEmpty();

            var validationResult = await ValidateAsync(context.Entity, cancellationToken);

            return(validationResult.ToValidationMessage());
        }
Beispiel #7
0
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <OutboxEvent> context, CancellationToken cancellationToken = default)
        {
            RuleFor(customer => customer.Type).NotEmpty();
            RuleFor(customer => customer.Event).NotEmpty();

            var validationResult = await ValidateAsync(context.Entity, cancellationToken);

            return(validationResult.ToValidationMessage());
        }
Beispiel #8
0
        public Task ExecuteAsync(HookType type, IDomainEntityContext <UserPreferences> context, CancellationToken cancellationToken)
        {
            if (context.Entity.Id == Guid.Empty)
            {
                context.Entity.Id = _currentUser.Id;
            }

            return(Task.CompletedTask);
        }
        public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken)
        {
            context.Entity.UserRoles?.ForEach(x =>
            {
                x.User = null;
                x.Role = null;
            });

            return(Task.CompletedTask);
        }
Beispiel #10
0
        public Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken)
        {
            int i = 0;

            foreach (var invoiceLine in context.Entity.InvoiceLines)
            {
                invoiceLine.LineNumber = ++i;
            }

            return(Task.CompletedTask);
        }
        public Task ExecuteAsync(HookType type, IDomainEntityContext <Customer> context, CancellationToken cancellationToken)
        {
            // Example: Retrieve variable set in application layer.
            // var exampleBoolean = context.State.Get<bool>(CustomerStateKeys.ExampleBoolean);
            // or
            var hasExampleBoolean = context.State.TryGet(CustomerStateKeys.ExampleBoolean, out bool exampleBoolean2);

            // Example: Set variable accesible in application layer after create, update or delete has been called.
            context.State.Set(CustomerStateKeys.ExampleGuid, Guid.NewGuid());

            return(Task.CompletedTask);
        }
        internal static bool IsPropertyDirty <TEntity, TProperty>(this IDomainEntityContext <TEntity> context, Expression <Func <TEntity, TProperty> > property)
            where TEntity : IEntity
            where TProperty : IComparable
        {
            var currentValue = context.Entity != null?property.Compile()(context.Entity) : default;

            var previousValue = context.Pristine != null?property.Compile()(context.Pristine) : default;

            var comparer = EqualityComparer <TProperty> .Default;

            return(!comparer.Equals(currentValue, previousValue));
        }
Beispiel #13
0
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Customer> context, CancellationToken cancellationToken = default)
        {
            RuleFor(customer => customer.Name).NotEmpty();
            RuleFor(customer => customer.Name).MaximumLength(200);
            RuleFor(customer => customer.InvoiceEmailAddress).MaximumLength(320);

            // How to: Scope validation rules to specific edit mode
            //When(x => context.EditMode == EditMode.Update, () =>
            //{
            //});

            var validationResult = await ValidateAsync(context.Entity, cancellationToken);

            return(validationResult.ToValidationMessage());
        }
Beispiel #14
0
        public async Task ExecuteAsync(HookType type, IDomainEntityContext <UserPreferences> context, CancellationToken cancellationToken)
        {
            switch (context.EditMode)
            {
            case EditMode.Create:
            case EditMode.Update:
                await context.AddEventAsync(UserPreferencesUpdatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken);

                break;

            case EditMode.Delete:
                await context.AddEventAsync(UserPreferencesDeletedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken);

                break;
            }
        }
        public Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Invoice> context, CancellationToken cancellationToken = default)
        {
            if (context.EditMode != EditMode.Delete)
            {
                return(ValidationResultTask.Ok());
            }

            var statussesAllowedToDelete = new[] { InvoiceStatus.Draft, InvoiceStatus.Cancelled };

            if (!statussesAllowedToDelete.Contains(context.Pristine.Status))
            {
                return(ValidationResultTask.Invalid("Not allowed to delete invoice in current status."));
            }

            return(ValidationResultTask.Ok());
        }
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Invoice> context, CancellationToken cancellationToken = default)
        {
            RuleFor(invoice => invoice.CustomerId).NotEmpty();
            RuleFor(invoice => invoice.InvoiceDate).GreaterThan(DateTime.MinValue);
            RuleFor(invoice => invoice.OrderReference).NotEmpty();
            RuleFor(invoice => invoice.InvoiceLines).NotEmpty();
            RuleForEach(invoice => invoice.InvoiceLines).ChildRules(invoiceLine =>
            {
                invoiceLine.RuleFor(x => x.LineNumber).NotEmpty();
                invoiceLine.RuleFor(x => x.Quantity).NotEmpty();
                invoiceLine.RuleFor(x => x.Description).NotEmpty();
            });

            var validationResult = await ValidateAsync(context.Entity, cancellationToken);

            return(validationResult.ToValidationMessage());
        }
Beispiel #17
0
        public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken)
        {
            switch (context.EditMode)
            {
            case EditMode.Create:
                context.AddEventAsync(UserCreatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken);
                break;

            case EditMode.Update:
                context.AddEventAsync(UserUpdatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken);
                break;

            case EditMode.Delete:
                context.AddEventAsync(UserDeletedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken);
                break;
            }
            return(Task.CompletedTask);
        }
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <User> context, CancellationToken cancellationToken = default)
        {
            RuleFor(user => user.Email).NotEmpty();
            RuleFor(user => user.Email).EmailAddress();
            RuleFor(user => user.FamilyName).NotEmpty();
            RuleFor(user => user.UserRoles).NotEmpty();
            RuleForEach(user => user.UserRoles).ChildRules(userRole =>
            {
                userRole.RuleFor(x => x.RoleId).NotEmpty();
            });
            RuleFor(user => user.UserRoles)
            .Must((userRoles) => HasUniqueRoles(userRoles))
            .WithMessage(x => "UserRoles cannot contain duplicate roles");

            var validationResult = await ValidateAsync(context.Entity, cancellationToken);

            return(validationResult.ToValidationMessage());
        }
Beispiel #19
0
        public async Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken)
        {
            if (type == HookType.BeforeCreate)
            {
                // An invoice always has an accompanying PDF document. We can create this by requesting synchronization via a domain event.
                context.Entity.PdfIsSynced = false;
                await context.AddMessageAsync(SynchronizeInvoicePdfMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
            }
            else if (type == HookType.BeforeUpdate && context.Pristine.Status == InvoiceStatus.Draft)
            {
                var invoiceToPdfModel = await _invoiceToPdfModelMapper.MapAsync(context.Entity, cancellationToken);

                var checksum = invoiceToPdfModel.GetChecksum();

                // Compare calculated checksum against checksum when PDF was last generated
                if (!string.Equals(checksum, context.Entity.PdfChecksum))
                {
                    context.State.TryGet(InvoiceStateKeys.ThrowIfPdfIsNotSynced, out bool throwIfPdfIsNotSynced);

                    if (throwIfPdfIsNotSynced)
                    {
                        // Prevent a possible infinite loop by not allowing synchronization when in the context of
                        // updating the invoice directly after PDF synchronization has occured. In this scenario the
                        // checksums should have been equal.
                        throw new DomainException("Expected invoice PDF to have been synced");
                    }

                    // Changes made to entity DO affect PDF content. Requesting synchronization.
                    context.Entity.PdfIsSynced = false;
                    await context.AddMessageAsync(SynchronizeInvoicePdfMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
                }
                else
                {
                    // Changes made to entity DIDNT affect PDF content. Not requesting synchronization.
                    context.Entity.PdfIsSynced = true;
                }
            }

            if (context.Entity.PdfIsSynced && context.IsPropertyDirty(x => x.PdfIsSynced))
            {
                await context.AddEventAsync(InvoicePdfSynchronizedEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
            }
        }
Beispiel #20
0
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Customer> context, CancellationToken cancellationToken = default)
        {
            if (context.EditMode != EditMode.Delete)
            {
                return(ValidationResult.Ok());
            }

            var invoiceStatussesWhichAllowDeletionOfCustomer = new[] { InvoiceStatus.Cancelled };

            var hasInvoices = await _invoiceQuery.AsQueryable()
                              .Where(x => x.CustomerId == context.Entity.Id)
                              .Where(x => !invoiceStatussesWhichAllowDeletionOfCustomer.Contains(x.Status))
                              .AnyAsync(cancellationToken);

            if (hasInvoices)
            {
                return(ValidationResult.Invalid("Cannot delete customer, because one or more invoices are linked to this customer."));
            }

            return(ValidationResult.Ok());
        }
        public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Role> context, CancellationToken cancellationToken = default)
        {
            if (context.EditMode == EditMode.Delete)
            {
                return(ValidationResult.Ok());
            }

            if (context.IsPropertyDirty(x => x.Name))
            {
                var alreadyExists = await _roleQuery.AsQueryable()
                                    .Where(x => x.Id != context.Entity.Id)
                                    .Where(x => x.Name == context.Entity.Name)
                                    .AnyAsync(cancellationToken);

                if (alreadyExists)
                {
                    return(ValidationResult.Invalid($"A role with name '{context.Entity.Name}' already exists."));
                }
            }

            return(ValidationResult.Ok());
        }
        public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken)
        {
            if (context.IsPropertyDirty(x => x.Email))
            {
                context.AddMessageAsync(SyncEmailToAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
            }

            if (context.IsPropertyDirty(x => x.Fullname))
            {
                context.AddMessageAsync(SyncNameToAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
            }

            if (!Enumerable.SequenceEqual(
                    context.Entity.UserRoles.Select(x => x.RoleId),
                    context.Pristine.UserRoles.Select(x => x.RoleId)
                    ))
            {
                context.AddMessageAsync(SyncRolesToAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
            }

            return(Task.CompletedTask);
        }
        public DomainEntity(
            ILogger logger,
            ICurrentUser currentUser,
            IDateTime dateTime,
            IDbCommand <T> dbCommand,
            Lazy <IEnumerable <IDefaultValuesSetter <T> > > defaultValuesSetters,
            Lazy <IEnumerable <IValidator <T> > > validators,
            Lazy <IEnumerable <IBeforeCreate <T> > > beforeCreateHooks,
            Lazy <IEnumerable <IAfterCreate <T> > > afterCreateHooks,
            Lazy <IEnumerable <IBeforeUpdate <T> > > beforeUpdateHooks,
            Lazy <IEnumerable <IAfterUpdate <T> > > afterUpdateHooks,
            Lazy <IEnumerable <IBeforeDelete <T> > > beforeDeleteHooks,
            Lazy <IEnumerable <IAfterDelete <T> > > afterDeleteHooks,
            Lazy <IOutboxEventCreator> outboxEventCreator,
            Lazy <IOutboxMessageCreator> outboxMessageCreator,
            Lazy <IJsonService <T> > jsonService,
            Lazy <IAuditlogger <T> > auditlogger
            )
        {
            Context     = new DomainEntityContext <T>(logger, outboxEventCreator, outboxMessageCreator, jsonService);
            Logger      = logger;
            CurrentUser = currentUser;
            DateTime    = dateTime;
            DbCommand   = dbCommand;

            _defaultValuesSetters = defaultValuesSetters;
            _validators           = validators;
            _beforeCreateHooks    = beforeCreateHooks;
            _afterCreateHooks     = afterCreateHooks;
            _beforeUpdateHooks    = beforeUpdateHooks;
            _afterUpdateHooks     = afterUpdateHooks;
            _beforeDeleteHooks    = beforeDeleteHooks;
            _afterDeleteHooks     = afterDeleteHooks;
            _auditlogger          = auditlogger;

            _options = new DomainEntityOptions();
        }
Beispiel #24
0
        public Task ExecuteAsync(HookType type, IDomainEntityContext <Customer> context, CancellationToken cancellationToken)
        {
            context.Entity.Invoices = null;

            return(Task.CompletedTask);
        }
        public Task ExecuteAsync(HookType type, IDomainEntityContext <Role> context, CancellationToken cancellationToken)
        {
            context.Entity.UserRoles = null;

            return(Task.CompletedTask);
        }
Beispiel #26
0
 public Task ExecuteAsync(HookType type, IDomainEntityContext <T> context, CancellationToken cancellationToken)
 {
     _logger.LogInformation($"{type}: {typeof(T).Name}");
     return(Task.CompletedTask);
 }
Beispiel #27
0
 public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken)
 {
     context.AddMessageAsync(DeleteAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken);
     return(Task.CompletedTask);
 }
 public async Task ExecuteAsync(HookType type, IDomainEntityContext <FeatureFlagSettings> context, CancellationToken cancellationToken)
 {
     await context.AddEventAsync(FeatureFlagSettingsUpdatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken);
 }
Beispiel #29
0
 public Task ExecuteAsync(HookType type, IDomainEntityContext <OutboxEvent> context, CancellationToken cancellationToken)
 {
     _outboxEventCreatedEvents.Add(OutboxEventCreatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id));
     return(Task.CompletedTask);
 }