From 5a57c5cde9fb0518f225e9bd1ca4c85f7f2787db Mon Sep 17 00:00:00 2001 From: Jezz Santos Date: Thu, 4 Apr 2024 10:35:33 +1300 Subject: [PATCH] Updated Notification eventing. Closes #30. Improves on #15. --- .../0050-domain-driven-design.md | 108 ++- docs/images/Persistence-Eventing.png | Bin 100655 -> 101424 bytes docs/images/Sources.pptx | Bin 900746 -> 914250 bytes docs/images/Subdomains.png | Bin 93459 -> 112410 bytes src/AncillaryDomain/Events.cs | 24 +- .../EventStreamChangeEvent.cs | 2 +- .../IEndUsersService.cs | 3 - .../IOrganizationsService.cs | 4 - .../IUserProfilesService.cs | 8 - src/BookingsDomain.UnitTests/TripSpec.cs | 3 +- src/BookingsDomain/BookingRoot.cs | 2 +- src/BookingsDomain/Events.cs | 39 +- .../CarsApplicationSpec.cs | 8 +- src/CarsApplication/CarsApplication.cs | 2 +- .../Persistence/ReadModels/Car.cs | 3 +- src/CarsDomain/CarRoot.cs | 4 +- src/CarsDomain/Events.cs | 37 +- .../Persistence/CarRepository.cs | 3 +- .../ChangeEventTypeMigratorSpec.cs | 25 +- .../Domain.Common.UnitTests.csproj | 1 + .../Entities/AggregateRootBaseSpec.cs | 4 +- .../Entities/TestAggregateRoot.cs | 18 +- src/Domain.Common/DomainEvent.cs | 31 + .../Entities/AggregateRootBase.cs | 12 +- src/Domain.Common/Events/Global.cs | 20 +- .../Ancillary/Audits/Created.cs | 19 +- .../Ancillary/EmailDelivery/Created.cs | 17 +- .../EmailDelivery/DeliveryAttempted.cs | 17 +- .../Ancillary/EmailDelivery/DeliveryFailed.cs | 17 +- .../EmailDelivery/DeliverySucceeded.cs | 17 +- .../EmailDelivery/EmailDetailsChanged.cs | 19 +- .../Bookings/CarChanged.cs | 19 +- src/Domain.Events.Shared/Bookings/Created.cs | 17 +- .../Bookings/ReservationMade.cs | 19 +- .../Bookings/TripAdded.cs | 23 +- .../Bookings/TripBegan.cs | 19 +- .../Bookings/TripEnded.cs | 19 +- src/Domain.Events.Shared/Cars/Created.cs | 20 +- .../Cars/ManufacturerChanged.cs | 19 +- .../Cars/OwnershipChanged.cs | 19 +- .../Cars/RegistrationChanged.cs | 22 +- .../Cars/UnavailabilitySlotAdded.cs | 19 +- .../Cars/UnavailabilitySlotRemoved.cs | 19 +- src/Domain.Events.Shared/EndUsers/Created.cs | 22 +- .../EndUsers/GuestInvitationAccepted.cs | 19 +- .../EndUsers/GuestInvitationCreated.cs | 19 +- .../EndUsers/MembershipAdded.cs | 19 +- .../EndUsers/MembershipDefaultChanged.cs | 19 +- .../EndUsers/MembershipFeatureAssigned.cs | 19 +- .../EndUsers/MembershipRoleAssigned.cs | 19 +- .../EndUsers/PlatformFeatureAssigned.cs | 17 +- .../EndUsers/PlatformRoleAssigned.cs | 17 +- .../EndUsers/PlatformRoleUnassigned.cs | 17 +- .../EndUsers/Registered.cs | 26 +- .../Identities/APIKeys/Created.cs | 19 +- .../Identities/APIKeys/KeyVerified.cs | 17 +- .../Identities/APIKeys/ParametersChanged.cs | 19 +- .../Identities/AuthTokens/Created.cs | 17 +- .../Identities/AuthTokens/TokensChanged.cs | 19 +- .../Identities/AuthTokens/TokensRefreshed.cs | 19 +- .../Identities/AuthTokens/TokensRevoked.cs | 17 +- .../PasswordCredentials/AccountLocked.cs | 15 +- .../PasswordCredentials/AccountUnlocked.cs | 15 +- .../Identities/PasswordCredentials/Created.cs | 17 +- .../PasswordCredentials/CredentialsChanged.cs | 17 +- .../PasswordResetCompleted.cs | 19 +- .../PasswordResetInitiated.cs | 17 +- .../PasswordCredentials/PasswordVerified.cs | 19 +- .../RegistrationChanged.cs | 19 +- .../RegistrationVerificationCreated.cs | 17 +- .../RegistrationVerificationVerified.cs | 15 +- .../Identities/SSOUsers/Created.cs | 19 +- .../Identities/SSOUsers/TokensUpdated.cs | 19 +- .../Organizations/Created.cs | 21 +- .../Organizations/SettingCreated.cs | 19 +- .../Organizations/SettingUpdated.cs | 19 +- .../UserProfiles/ContactAddressChanged.cs | 19 +- .../UserProfiles/Created.cs | 19 +- .../UserProfiles/DisplayNameChanged.cs | 19 +- .../UserProfiles/EmailAddressChanged.cs | 19 +- .../UserProfiles/NameChanged.cs | 19 +- .../UserProfiles/PhoneNumberChanged.cs | 19 +- .../UserProfiles/TimezoneChanged.cs | 19 +- .../Entities/IDomainEvent.cs | 2 +- .../Cars}/CarStatus.cs | 2 +- .../EndUsers/RegisteredUserProfile.cs | 12 + src/Domain.Shared/EndUsers/UserAccess.cs | 7 + .../EndUsers}/UserClassification.cs | 2 +- src/Domain.Shared/EndUsers/UserStatus.cs | 7 + ...sersApplication.DomainEventHandlersSpec.cs | 100 +++ .../EndUsersApplication.UnitTests.csproj | 1 + .../EndUsersApplicationSpec.cs | 231 +++-- .../InvitationsApplicationSpec.cs | 4 +- ...EndUsersApplication.DomainEventHandlers.cs | 25 + .../EndUsersApplication.cs | 220 +++-- ...EndUsersApplication.DomainEventHandlers.cs | 11 + .../IEndUsersApplication.cs | 5 +- .../InvitationsApplication.cs | 1 + .../Persistence/IEndUserRepository.cs | 2 + .../Persistence/ReadModels/EndUser.cs | 7 +- .../Persistence/ReadModels/Invitation.cs | 3 +- .../ReadModels/MembershipJoinInvitation.cs | 13 +- .../EndUserRootSpec.cs | 39 +- src/EndUsersDomain/EndUserProfile.cs | 63 ++ src/EndUsersDomain/EndUserRoot.cs | 19 +- src/EndUsersDomain/Events.cs | 73 +- src/EndUsersDomain/UserAccess.cs | 7 - src/EndUsersDomain/UserStatus.cs | 7 - .../EndUsersApiSpec.cs | 17 +- .../StubEventNotificationMessageBroker.cs | 20 + .../EndUsersInProcessServiceClient.cs | 7 - .../EndUsersInfrastructure.csproj | 1 + src/EndUsersInfrastructure/EndUsersModule.cs | 13 +- .../EndUserDomainNotificationConsumer.cs | 34 + ...rIntegrationEventNotificationTranslator.cs | 36 + .../Notifications/EndUserNotifier.cs | 17 + .../Persistence/EndUserRepository.cs | 13 +- .../Persistence/InvitationRepository.cs | 5 +- .../ReadModels/EndUserProjection.cs | 3 +- src/IdentityDomain/Events.cs | 114 +-- ...structure.Eventing.Common.UnitTests.csproj | 1 + .../EventNotificationNotifierSpec.cs | 292 ++++--- .../Notifications/TestChangeEvent.cs | 10 - .../Notifications/TestDomainEvent.cs | 22 + .../Projections/ReadModelProjectorSpec.cs | 26 +- .../Projections/TestEvent.cs | 10 +- .../EventNotificationNotifier.cs | 108 ++- .../EventNotificationRegistration.cs | 14 +- .../Notifications/IntegrationEvent.cs | 25 + .../Notifications/NoOpConsumerRegistration.cs | 15 - ...Consumer.cs => NoOpDomainEventConsumer.cs} | 6 +- .../NoOpEventNotificationMessageBroker.cs | 17 + .../NoOpEventNotificationRegistration.cs | 16 + ...pIntegrationEventNotificationTranslator.cs | 20 + .../PassThroughEventNotificationProducer.cs | 20 - .../Projections/ReadModelProjector.cs | 2 +- .../Resources.Designer.cs | 11 +- .../Resources.resx | 5 +- .../IDomainEventNotificationConsumer.cs | 15 + .../IEventNotificationConsumer.cs | 15 - .../IEventNotificationMessageBroker.cs | 14 + .../IEventNotificationProducer.cs | 22 - .../IEventNotificationRegistration.cs | 10 +- .../Notifications/IIntegrationEvent.cs | 17 + ...IIntegrationEventNotificationTranslator.cs | 22 + .../Eventing/EventHandlerBaseSpec.cs | 18 +- ...ProcessSynchronousNotificationRelaySpec.cs | 22 +- ...{TestConsumer.cs => TestDomainConsumer.cs} | 8 +- .../Notifications/TestMessageBroker.cs | 18 + .../ApplicationServices/Eventing/TestEvent.cs | 12 +- ...astructure.Hosting.Common.UnitTests.csproj | 1 + .../InProcessSynchronousNotificationRelay.cs | 3 +- .../Extensions/EventingExtensions.cs | 3 +- .../EventNotifyingStoreExtensions.cs | 2 +- .../ExampleEventNotificationMessageBroker.cs | 40 + .../Infrastructure.Shared.csproj | 2 + .../Extensions/HostExtensions.cs | 13 +- .../Extensions/WebApplicationExtensions.cs | 2 +- .../EndUsers/PersonRegistered.cs | 25 + .../Integration.Events.Shared.csproj | 18 + ...ionsApplication.DomainEventHandlersSpec.cs | 105 +++ .../OrganizationsApplication.UnitTests.csproj | 1 + .../OrganizationsApplicationSpec.cs | 34 - ...izationsApplication.DomainEventHandlers.cs | 11 + .../IOrganizationsApplication.cs | 5 +- ...izationsApplication.DomainEventHandlers.cs | 26 + .../OrganizationsApplication.cs | 111 ++- src/OrganizationsDomain/Events.cs | 16 +- src/OrganizationsDomain/OrganizationRoot.cs | 2 - .../OrganizationsInProcessServiceClient.cs | 8 - .../OrganizationNotificationConsumer.cs | 34 + .../Notifications/OrganizationNotifier.cs | 18 + .../OrganizationsModule.cs | 13 +- src/SaaStack.sln | 9 + src/SaaStack.sln.DotSettings | 23 +- .../AnalyzerConstants.cs | 1 + .../Extensions/SymbolExtensions.cs | 53 ++ .../Extensions/SyntaxFilterExtensions.cs | 156 +++- .../DomainDrivenDesignAnalyzerSpec.cs | 372 +++++++- .../EventingAnalyzerSpec.cs | 819 ++++++++++++++++++ .../AnalyzerReleases.Shipped.md | 9 +- .../DomainDrivenDesignAnalyzer.cs | 23 +- .../EventingAnalyzer.cs | 190 ++++ .../Extensions/SyntaxExtensions.cs | 18 - .../Resources.Designer.cs | 89 +- .../Resources.resx | 36 +- .../Tools.Analyzers.NonPlatform.csproj | 6 + ...fileApplication.DomainEventHandlersSpec.cs | 142 +++ .../UserProfileApplicationSpec.cs | 121 +-- .../UserProfilesApplication.UnitTests.csproj | 1 + ...ProfilesApplication.DomainEventHandlers.cs | 11 + .../IUserProfilesApplication.cs | 7 +- ...ProfilesApplication.DomainEventHandlers.cs | 26 + .../UserProfilesApplication.cs | 221 ++--- src/UserProfilesDomain/Events.cs | 28 +- .../UserProfilesInProcessServiceClient.cs | 18 - .../UserProfileNotificationConsumer.cs | 34 + .../Notifications/UserProfileNotifier.cs | 18 + .../UserProfilesModule.cs | 13 +- 199 files changed, 4613 insertions(+), 1721 deletions(-) create mode 100644 src/Domain.Common/DomainEvent.cs rename src/{CarsDomain => Domain.Shared/Cars}/CarStatus.cs (69%) create mode 100644 src/Domain.Shared/EndUsers/RegisteredUserProfile.cs create mode 100644 src/Domain.Shared/EndUsers/UserAccess.cs rename src/{EndUsersDomain => Domain.Shared/EndUsers}/UserClassification.cs (80%) create mode 100644 src/Domain.Shared/EndUsers/UserStatus.cs create mode 100644 src/EndUsersApplication.UnitTests/EndUsersApplication.DomainEventHandlersSpec.cs create mode 100644 src/EndUsersApplication/EndUsersApplication.DomainEventHandlers.cs create mode 100644 src/EndUsersApplication/IEndUsersApplication.DomainEventHandlers.cs create mode 100644 src/EndUsersDomain/EndUserProfile.cs delete mode 100644 src/EndUsersDomain/UserAccess.cs delete mode 100644 src/EndUsersDomain/UserStatus.cs create mode 100644 src/EndUsersInfrastructure.IntegrationTests/Stubs/StubEventNotificationMessageBroker.cs create mode 100644 src/EndUsersInfrastructure/Notifications/EndUserDomainNotificationConsumer.cs create mode 100644 src/EndUsersInfrastructure/Notifications/EndUserIntegrationEventNotificationTranslator.cs create mode 100644 src/EndUsersInfrastructure/Notifications/EndUserNotifier.cs delete mode 100644 src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestChangeEvent.cs create mode 100644 src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestDomainEvent.cs create mode 100644 src/Infrastructure.Eventing.Common/Notifications/IntegrationEvent.cs delete mode 100644 src/Infrastructure.Eventing.Common/Notifications/NoOpConsumerRegistration.cs rename src/Infrastructure.Eventing.Common/Notifications/{NoOpConsumer.cs => NoOpDomainEventConsumer.cs} (55%) create mode 100644 src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationMessageBroker.cs create mode 100644 src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationRegistration.cs create mode 100644 src/Infrastructure.Eventing.Common/Notifications/NoOpIntegrationEventNotificationTranslator.cs delete mode 100644 src/Infrastructure.Eventing.Common/Notifications/PassThroughEventNotificationProducer.cs create mode 100644 src/Infrastructure.Eventing.Interfaces/Notifications/IDomainEventNotificationConsumer.cs delete mode 100644 src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationConsumer.cs create mode 100644 src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationMessageBroker.cs delete mode 100644 src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationProducer.cs create mode 100644 src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEvent.cs create mode 100644 src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEventNotificationTranslator.cs rename src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/{TestConsumer.cs => TestDomainConsumer.cs} (56%) create mode 100644 src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestMessageBroker.cs create mode 100644 src/Infrastructure.Shared/Eventing/Notifications/ExampleEventNotificationMessageBroker.cs create mode 100644 src/Integration.Events.Shared/EndUsers/PersonRegistered.cs create mode 100644 src/Integration.Events.Shared/Integration.Events.Shared.csproj create mode 100644 src/OrganizationsApplication.UnitTests/OrganizationsApplication.DomainEventHandlersSpec.cs create mode 100644 src/OrganizationsApplication/IOrganizationsApplication.DomainEventHandlers.cs create mode 100644 src/OrganizationsApplication/OrganizationsApplication.DomainEventHandlers.cs create mode 100644 src/OrganizationsInfrastructure/Notifications/OrganizationNotificationConsumer.cs create mode 100644 src/OrganizationsInfrastructure/Notifications/OrganizationNotifier.cs create mode 100644 src/Tools.Analyzers.NonPlatform.UnitTests/EventingAnalyzerSpec.cs create mode 100644 src/Tools.Analyzers.NonPlatform/EventingAnalyzer.cs create mode 100644 src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs create mode 100644 src/UserProfilesApplication/IUserProfilesApplication.DomainEventHandlers.cs create mode 100644 src/UserProfilesApplication/UserProfilesApplication.DomainEventHandlers.cs create mode 100644 src/UserProfilesInfrastructure/Notifications/UserProfileNotificationConsumer.cs create mode 100644 src/UserProfilesInfrastructure/Notifications/UserProfileNotifier.cs diff --git a/docs/design-principles/0050-domain-driven-design.md b/docs/design-principles/0050-domain-driven-design.md index 568b127c..fa7bc2f9 100644 --- a/docs/design-principles/0050-domain-driven-design.md +++ b/docs/design-principles/0050-domain-driven-design.md @@ -4,7 +4,7 @@ ## Design Principles -1. We want to design our software according to the use cases we understand from the real world. Thus, focusing on [modeling behaviors](../decisions/0040-modeling.md) in the software rather than modeling the data required to enable the software to operate (a.k.a Data-Modeling). +1. We want to design our software according to the use cases we understand from the real world. Thus, focusing on [modeling behaviors](../decisions/0040-modeling.md) in the software rather than modeling the data (a.k.a Data-Modeling) required to enable the software to operate. Data modeling is too database and technology focused. 2. We want to define discrete boundaries of "state" changes, and thus, we want to use the notion of "aggregates" to do that where an aggregate represents the smallest atomic unit of state change. 3. We want to define at least one root aggregate per explicit subdomain. (Ideally, one per subdomain). 4. We want aggregates to be the source/producer of "domain events" that represent atomic units of change, and have them communicated to other subdomains (either: remotely over HTTP, or in-process), using a pub-sub mechanism. @@ -15,30 +15,31 @@ 9. When an Aggregate's state change is persisted, the domain events (that it created) will be published to any consumers registered to receive them. These consumers may be other subdomains in the same process, or running in other processes. 10. 11. We want to re-use the design principles of DDD, such as "AggregateRoots", "Entities", "ValueObjects", "Repositories", "Factories", "Subdomains" etc. Where: -1. All Aggregates, Entities, and ValueObjects will be instantiated by class factories. They will validate all data entering the domain. They will return `Result` for any validation errors. -2. They will ensure that no Aggregate ("Aggregate" being the graph of all child/ancestor Entities and ValueObjects) is ever in an invalid state at any time. -3. Aggregates will verify their own "invariants" whenever there is a state change. Invariants are the things about an aggregate that don't change. -4. ValueObjects will always be immutable and only "equal" based on their internal state. -5. Entities and Aggregates will be mutable, and "equal" by their unique identifier (despite differences in their internal value). -6. A subdomain defines the initial bounded context for a Root Aggregate. Other bounded contexts will evolve as the product evolves. -12. We want to leverage Hexagonal/Onion/Clean architecture principles: -1. The Application Layer defines one (or more) external interfaces/contracts for the Subdomain. -2. All dependencies only point inward. i.e., The Domain Layer strictly has no dependency on the Application Layer, nor on the Infrastructure Layer. In contrast, the Infrastructure Layer and Application Layer can have dependencies on the Domain Layer (directly or indirectly). -3. We want to avoid building [Transaction Scripts (and anemic domain models)](https://martinfowler.com/eaaCatalog/transactionScript.html) in the Application Layer, as that encourages tight coupling, and anemic domain models. -4. Application interfaces/contracts will be composed of commands and queries (CQRS): +12. All Aggregates, Entities, and ValueObjects will be instantiated by class factories. They will validate all data entering the domain. They will return `Result` for any validation errors. +13. They will ensure that no Aggregate ("Aggregate" being the graph of all child/ancestor Entities and ValueObjects) is ever in an invalid state at any time. +14. Aggregates will verify their own "invariants" whenever there is a state change. Invariants are the things about an aggregate that don't change. +15. ValueObjects will always be immutable and only "equal" based on their internal state. +16. Entities and Aggregates will be mutable, and "equal" by their unique identifier (despite differences in their internal value). +17. A subdomain defines the initial bounded context for a Root Aggregate. Other bounded contexts will evolve as the product evolves. +18. We want to leverage Hexagonal/Onion/Clean architecture principles: +19. The Application Layer defines one (or more) external interfaces/contracts for the Subdomain. +20. All dependencies only point inward. i.e., The Domain Layer strictly has no dependency on the Application Layer, nor on the Infrastructure Layer. In contrast, the Infrastructure Layer and Application Layer can have dependencies on the Domain Layer (directly or indirectly). +21. We want to avoid building [Transaction Scripts (and anemic domain models)](https://martinfowler.com/eaaCatalog/transactionScript.html) in the Application Layer, as that encourages tight coupling, and anemic domain models. +22. Application interfaces/contracts will be composed of commands and queries (CQRS): 1. For "commands" it will delegate the command to one (or more) Root Aggregates (i.e., that results in a change of state). - 2. For "queries" it will delegate the query directly to a read model in a DataStore. In rare cases, the query may involve an Aggregate to check access rules. -5. The Application Layer delegates all decisions (in a command or query) to an Aggregate. The only decisions that should exist in the Application Layer, are: - 1. The statelessness of the contract. Stateless or Stateful. i.e., Where to pull data from (which ApplicationService/Repository) and where to push it (which ApplicationService/Repository) and when. - 2. Which Aggregate use case to invoke, when. -6. The Application Layer is responsible for providing the Domain Layer with all the data it needs to execute the specific use case. -7. The Application Layer is responsible for converting all data it obtains (either from Repository/ApplicationService, or from its contract parameters) into ValueObjects that the Domain Layer requires. -8. The Application Layer will use the [Tell Don't Ask](https://martinfowler.com/bliki/TellDontAsk.html) pattern to instruct the Aggregate to execute the use case. -9. The Application Layer will convert all changed domain states (in any command or query responses) into shared DTOs (e.g., Resources) that can be shared with all ApplicationServices. e.g., A REST API is one such ApplicationService, as is any adapter to any 3rd party system. -10. We want to use [Dependency Injection](0060-dependency-injection.md) to inject the necessary dependencies into the Application Layer (ApplicationServices) and Domain Layer (DomainServices), such that they remain decoupled from adapter implementations. -11. There will be far fewer DomainServices than ApplicationServices. Both kinds of services will be designed in terms of the needs of the consuming subdomain component (and perhaps combined into a more general abstraction if being consumed by multiple subdomains). They shall be as domain-specific as possible and not be described in terms of the technology that implements them (upholding the SOLID principle of [ISP](https://en.wikipedia.org/wiki/Interface_segregation_principle)). -12. The Application Layer can be exposed by any number of interfaces (e.g., a REST API, a Queue, or a Service Bus) -13. We want to capture, measure, and monitor usage activity, diagnostics, and audit key events in the system by using the [Recorder](0030-recording.md). + 2. For "queries" it will delegate the query directly to a read model in an `IDataStore`. In rare cases, the query may involve an Aggregate to check access rules. +23. The Application Layer delegates all decisions (in a command or query) to an Aggregate. The only decisions that should exist in the Application Layer, are: + 1. The statelessness of the contract. Stateless or Stateful. + 2. Where to pull data from (which ApplicationService/Repository) and where to push it (which ApplicationService/Repository) and when. + 3. Which Aggregate use case to invoke, when. +24. The Application Layer is responsible for providing the Domain Layer with all the data it needs to execute the specific use case. Also, may require domain services for data processing. +25. The Application Layer is responsible for converting all data it obtains (either from Repository/ApplicationService, or from its contract parameters) into ValueObjects that the Domain Layer requires. Domain Layer does not accept variables in anything but primitives and ValueObjects. +26. The Application Layer will use the [Tell Don't Ask](https://martinfowler.com/bliki/TellDontAsk.html) pattern to instruct the Aggregate to execute the use case, no matter how complex the use case is (or isn't). The [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) also applies to reading any data from an Aggregate. +27. The Application Layer will convert all changed domain states (in any command or query responses) into shared DTOs (e.g., Resources) that can be shared with all other Application Services. e.g., A REST API is one such Application Service, as is any adapter to any 3rd party system. +28. We want to use [Dependency Injection](0060-dependency-injection.md) to inject the necessary dependencies into the Application Layer (as Application Services) and Domain Layer (as Domain Services), such that they remain decoupled from concrete adapter implementations. +29. We expect that there will be far fewer Domain Services than Application Services. Both kinds of services will be designed in terms of the needs of the consuming subdomain component (and perhaps combined into a more general abstraction if being consumed by multiple subdomains). They shall be as domain-specific as possible and not be described in terms of the technology that implements them (upholding the SOLID principle of [ISP](https://en.wikipedia.org/wiki/Interface_segregation_principle)). +30. The Application Layer can be exposed to any number of interfaces (e.g., a REST API, a Queue, or a Service Bus) or as consumer of domain events notifications. +31. We want to capture, measure, and monitor usage activity, diagnostics, and audit key events in the system by using the [Recorder](0030-recording.md). ## Implementation @@ -48,32 +49,36 @@ In DDD parlance, a "Generic" subdomain is one that is generic to all products. I A "Core" subdomain is unique to your product and a vital characteristic of it. -> That is not to say that the Generic subdomains are not vital; they are absolutely vital for operation, it is just that it is not vital that you build them yourself. +> That is not to say that the Generic subdomains are not vital; they are absolutely vital for operation. It is just that it is not vital that you build them yourself. Subdomains -For example, in this codebase, we have provided 2 "Core" subdomains (`Cars` and `Bookings`) that together define the larger "domain" of this example product to be `Car Sharing`. The other subdomains are all Generic subdomains: +Among the subdomains will exist one or more "bounded contexts". In DDD, a bounded context is simply a defined boundary within which the semantics of the aggregates and use cases in the subdomains are well-known and unambiguous. Given that in the real world, many aspects of the same thing/concept are only relevant in specific contexts. That is exactly what a bounded context is meant to be. A codified specific context, where meaning is clear, using names of things that can other meanings in different contexts. -- `EndUsers`, `Organizations`, `Profiles,` and `Subscriptions` are all generic subdomains related to the multi-tenancy and identity management within a typical SaaS product. -- `AuthN`, `Auxillary`, `Images` (and `Workers`) are all generic subdomains related to the operation of a typical SaaS product. +For example, in this codebase, we have provided two "Core" subdomains: `Cars` and `Bookings`. Together, these two subdomains alone are understood together to represent the "core" of a bounded context of borrowing cars with the "Car Sharing" domain. + +The other subdomains are all "Generic" subdomains, and form another more technical bounded context where they work together to provide consistency and rules that govern things like multi-tenancy, ownership and payment authority, etc: + +- `EndUsers`, `Organizations`, `UserProfiles`, `Identities`, and `Subscriptions` are all generic subdomains related to the multi-tenancy and identity management within a typical SaaS product. +- `Ancillary`, `Images` (and `Workers`) are all generic subdomains related to the operation of a typical SaaS product. #### Your first few Subdomains -When starting a new product, many developers who are unfamiliar with DDD wonder how to get started defining the new core subdomains of their products. It is a fair question. They are (more often than not) used to thinking about software design in terms of the data tables, columns, and datatypes required by relational databases (a.k.a data modeling). So, they expect that there is some similar way of working in DDD that easily translates. This is not really the case. +When starting a new product, many developers who are unfamiliar with DDD wonder how to start defining the new core subdomains of their products. It is a fair question. They are (more often than not) used to thinking about software design in terms of the data tables, columns, and data types required by relational databases (a.k.a. data modeling). So, they expect that there is some similar way of working in DDD that can be easily translated. This is not really the case. -There has been a lot of work done on what is called "strategic" design in DDD. Whole books have been written on it; some like [Eric Evans's "blue book"](https://www.domainlanguage.com/ddd/) are very comprehensive and well worth the read. They guide you through designing the subdomains and bounded contexts of your software, using context maps and things like that. Most of that material makes a lot of sense, but getting started with applying it before you have any software built is still quite challenging, particularly when building software products. +A lot of work has been done on what is called "strategic" design in DDD. Whole books have been written on it; some like [Eric Evans's "blue book"](https://www.domainlanguage.com/ddd/) are very comprehensive and well worth the read. They guide you through understanding and designing the subdomains and bounded contexts of your software using context maps and things like that. Most of that material makes a lot of sense, but getting started with applying it before you have any software built is still quite challenging, particularly when building software products from scratch. However, what is not so hard is answering this question: -Q1. What are the use cases for using the software you want to build? a.k.a What, specifically, should your software do? +Q. What are the use cases for using the software you want to build? a.k.a What, specifically, should your software do? If you can answer that question, then you are already designing the "Subdomains" of your domain. -We recommend that you just get started focusing on your use cases, and the database design will fall out of the back of that process. That would be very scary and risky if you were data modeling, but you are not doing that now, you are domain modeling instead. +We recommend that you just get started focusing on your use cases, and the database design will fall out of the back of that process. It would be very scary and risky if you were data modeling, but you are not doing that now; you are doing "domain modeling" instead. Just take the answer to the question above, then group the use cases into groups around a common concept, and then make each one of those concepts a new subdomain. -What should you call the subdomains? +Q. What should you call the subdomains? Well, best call them the same things that you use to describe them in the real world. This will eventually become your ubiquitous language. @@ -81,7 +86,7 @@ Well, best call them the same things that you use to describe them in the real w > > So, for example, it is relatively safe to use the simple noun "Car" to represent the physical object (of ~30,000 parts, the thing that one gets in and drives away), even though you are only modeling very basic details about it, like its color, mileage, location, make, model and year, number of windows, car seats, etc. However, what if your specific business decides to include using bicycles, boats, buses or trucks later on? Would "Vehicle" have been a better option to start with? Context matters. -In the "Car Sharing" world (the example domain in the codebase that you are learning from), we know that this domain is all about people (end-users) making reservations (bookings on some future schedule) to use a car (owned by someone else), finding the car at some location, then gaining access to the car (digital or physical key), then driving the car on some journey over some route for some time, eventually, returning the car to some location, and being charged for its use. +In the example "Car Sharing" world (the example domain in the codebase that you are learning from), we know that this domain is all about people (end-users) making reservations (bookings on some future schedule) to use a car (owned by someone else), finding the car at some location, then gaining access to the car (digital or physical key), then driving the car on some journey over some route for some time, eventually, returning the car to some location, and being charged for its use. So, from that simple "mental model" of the real world, we know that we are likely to have these initial core subdomains: @@ -102,7 +107,7 @@ For the proposed subdomains that don't have use cases specifically yet, they won > For example, you may not be able to come up with a use case for manipulating a Location or a Trip just yet, or for that matter, an Unavailability. That is fine. A car may go on a Trip and have a start and end Location, and while it is booked, it is Unavailable for hire by someone else. But this does not mean that those concepts need to be subdomains in their own rights. Indeed, Unavailability has no meaning without the context of a Car, so it is probably not separate from a Car. -So, Unavailability starts out as an entity of a Car, and Location can start out as two value objects of a Booking, i.e, PickUpLocation and DropoffLocation. Trips could be a child entity of a Booking since you can do several of them during a Booking. +So, "Unavailability" starts out as an entity of a Car, and "Location" can start out as two value objects of a Booking, i.e., "Pick up" location and "Drop off" Location. Trips could be a child entity of a Booking since you can do several of them during a Booking. As time goes on, and as you explore your product opportunities further, it is very common that subdomains both diverge (and split up) or converge (and merge together). This is very normal, and unavoidable, and unpredictable, and unknowable ahead of time. So we recommend that you don't try to second guess the future since it is likely to change as you move forward. @@ -117,9 +122,9 @@ A quick note about [Bounded Contexts](https://martinfowler.com/bliki/BoundedCont Bounded Contexts are another vital concept in Strategic DDD design, and often one that is difficult to implement in code. -Your software does not need to **explicitly** model its bounded contexts for the sake of it. +Your software does not actually need to **explicitly** model its bounded contexts for the sake of it, nor for completeness. -> They are more of a high-level design attribute than anything else, most useful in context maps. +> They are more of a high-level design attribute than anything else, and they are most useful in context maps used for design and solving design problems. > > Yes, they are useful for drawing context maps and the like (at later dates), but they are not necessary to get started on modeling the real world with DDD design. @@ -175,7 +180,7 @@ public sealed class CarRoot : AggregateRootBase #### Creation -When a new aggregate is created, it must be constructed with a class factory method. This method will then be called from the Application Layer when a new aggregate needs to be created. +When a new aggregate is created, it must be constructed using a class factory method. This method will then be called from the Application Layer when a new aggregate needs to be created. > Instantiating an aggregate using a constructor directly is not permitted from outside of the domain. @@ -440,7 +445,7 @@ Aggregates may have conditions within states that are "invariant". That is, thin Sometimes, those invariants are only true at certain times (in certain states). Sometimes, they are true at all times (in all states). -> For example, for a Booking to be made for a Car, the car must not only exist, but it must be roadworthy, as well as have availability at that time. Availability can change over time (as bookings are made), but roadworthiness may not change. When the car is first created (as an aggregate), it may not have the details it needs to pass the roadworthiness test yet. Those details may come in a future domain event, so the invariant can be applied only after that time. +> For example, for a Booking to be made for a Car, the car must not only exist, but it must be roadworthy, as well as have availability at that time. Availability can change over time (as bookings are made), but road worthiness may not change. When the car is first created (as an aggregate), it may not have the details it needs to pass the road worthiness test yet. Those details may come in a future domain event, so the invariant can be applied only after that time. Invariants are strictly checked/validated in three places in a subdomain: @@ -959,13 +964,34 @@ The only invariants that need verifying are when the value object is constructed ### Event Notifications -TODO: How do we notify consumers of produced domain events? +In the design of most distributed systems of the nature of this system (or, of systems that are expected to evolve into distributed systems later), it is common to decouple each of the subdomains from each other. De-coupling effectively is absolutely vital to allowing the system to change, grow and evolve over time. Lack of effective de-coupling (at the technical level) is the main reason most software systems devolve into big-balls-of-mud, simply because of coupling. + +There are several techniques for de-coupling your subdomains, including: separating layers, using ports and adapters, starting with a modular monoliths and decomposing it into microservices later etc. - +Another one of these techniques is the use of Event-Driven Architecture (EDA), where change in communicated within and across boundaries. + +EDA relies on the fact that your system will emit "domain events", that it can share both within specific bounded contexts (as "domain events"), and externally to other systems (as "integration events". + +> When sharing events within a bounded context (or within the same process) the process can remain consistent, we call these "domain events". +> +> When sharing events across bounded contexts (or across processes and hosts) these events are called "integration events". +In SaaStack: +1. We use "domain events" to communicate changes (within the Domain Layer) and within all aggregates and entities. Regardless of whether we are using event sourcing for persistence or not. +2. We publish all "domain events" whenever the state of any aggregate is saved in any repository, via the `EventSourcingDddCommandStore` or via the `SnapshottingDddCommandStore`. +3. We treat "domain events" and "integration events" slightly differently: + 1. "domain events" are published synchronously and handled synchronously after the aggregate is saved, and are always consistent. + 2. "integration events" are published synchronously, but are expected to be handled asynchronously (by a message broker) and be eventually consistent. +> We assume that all "domain events" are only ever published to other subdomains that are in the same "bounded context" and thus, also in the same host process. When this is not true, for example, if subdomains of the same bounded context are split into separate host processes, then these subdomains will need to communicate with "integration events" instead, and they will be eventually consistent. +The synchronous publication of all "domain events" is handled automatically by the `IEventNotifyingStoreNotificationRelay` (after events have first been projected by the `IEventNotifyingStoreProjectionRelay`). +![Eventing](../images/Persistence-Eventing.png) +Domain events are published synchronously (round-robin) one at a time: +1. First, to all registered `IDomainEventNotificationConsumer` consumers. These consumers can fail and report back errors that are captured synchronously. +2. Then to all registered `IIntegrationEventNotificationTranslator` translators, that have the option to translate o domain event into an integration event, or not. This translation can also fail, and report back errors that are captured synchronously. +3. Finally, if the translator translates a domain event into an integration event it is then published to the `IEventNotificationMessageBroker` that should send the integration event to some external message broker, who will deliver it asynchronous to external consumers. This can also fail, and report back errors that are captured synchronously diff --git a/docs/images/Persistence-Eventing.png b/docs/images/Persistence-Eventing.png index 08d0f2f92707ee156b0b825a9bd7819e3b673b09..e52e15158233d2a44b07eb7d582e7ae6496716a2 100644 GIT binary patch literal 101424 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ⅆ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℘Il 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

j1EyNJ%Z>SWz>yNlZ$_=<0 z{hl0Z81BY4-*=CQhL1~!Dx1|*ti)<@-LHlj#z{|dQCee%?e)5{sA04lxYkgwpRiyN z%$JA%M__~1Dp!kfgLcw_+S;!tMR`dboBzAf*QX#(DYHF?{_likx+$AZ^r5Ql?}#XN zuCwi%?(Cl=v#eoq!V?MfHEe^IRDPv37^=rY-s&bnE|C-QMomQDKAp3O4_)Lke~CYN zz|W<7${H9B#T{8=kUmF@8DGk%yoiE!8%iCkD;SgLB&a;lS zJAie3$wi~H<8X<~rH`WD{IaBaZ+TcXLApz-50N3&BK$=@+(6o4-sn5DJO$#{0^0lTsUj9Am^nkLbN=i3 z_DX(yLl@x_DcUY-li}FE$f_BITWmWmEy^f&!Ve6dOopi@kFMjo+u}w;=;NS*53rk*DZntbNi#Io* zENQ&+W~5>pOOT2e9N;zMvV4a1E*Dl&k(zUD3GK@_Ow$)h^Z#mQZo8y&@*F*Feb)9p zWnbOvV|QN{{Ft}8absXIpPgUmPm~zVm-VJ})iJV*Gvlh>l)G8VgBzHg>vk`zPNz?CZ*3%Rc2cy*668cvu{VpkOeNH*gdb66VdCW17kOReIn{3;Tfd4dSTPab=1HMb4NaQCmluF9fkQ?;@msZ~Hj#O%G~TFmlBwbP8jQugIB z#8yB+@A4w?)!$~;cNa%h`;IOpH{{Zx;k1x1x)4G%mI4A(<9^%*KGU3H)GE}Z6Tty{ z0!F*I-?FPoRl} zy)G=Vb+^OU|3D>vXg8GLa7)XuDB&Yek$62%@xHldCE_=mybH5}trNve1tFo#RVjmd#7qXLUqi3%A|^VY_sWIzFo!_bEhx;rvWq3E z^5RyLY?PD_d}-*?{FB>6_(*5U32Xq+|R(QddD*M#N%(fKNec#xF{Yx*8n|k2;nGX08a{?CNJSg>ml9%MRn$f# zPdNp=??33K^IR59G3CWxiHGIumsFECX$T}7xkhD>zZa_V-rv5`d#v5`AW-Sc2x1@E zjzM4INm_8{#(>8;XO^*#!~Y+tJn(2s`1`_9~^y^DE^E9DWG`y!J)-D8p;F@Q|jZy5B-Jo zDD~9`Xv}hYW`x4(gN=f~6zNRUxftZAnNZ>J;63B?m!DknZTji*Ok!eT!(lyYSC6pe zS&}q@EM#(eL(#c-!;%kch~9MQ)t}gqU^i;*dC759W(d4i`za{_v_#A{=EORkbV6_b)VEZMI!=*p zBuK(D*tqkON}mnW{+u=$i!wVY8W6^MYve^wG{b zuKjp}VTqTFi}QC=wIb42zg&sAyJesAuQ`k+9!Hp4$2CPvQ0ZMfYq^Snmcak_pSA+! zaZpXj*%=0W0xPR!G7FfSv%AK(NGt2UHWF;B8hi zcpx53f&AhEz9&GLDgNsrX*fc-|NdO4zyk;5@I(M-B3M$NBVd>am4s*{0J%@}X$Cf) z7(NLwCxPtV2OK~m{3e6|bxB~AJTD+X)+OkUkql);kd=f0wNMBVpblD)Kv}>y8FW(7 z1A3mg>k7E0LWu$L6eu48|2qhfD+)^OQ$VS7_`j?gFbk3x@y~d8GUg-y87cs16h{9u zGM|j9n12SlKI9qDmIB2FkW-5~>Q z>0dH)8k84;ko?cl6EqO5QXp`^lqrM?7)t}&rpn)xrg zDjh6mDeIqMWD9El)2_!1ur9G2fGY#43=z)#m)&mve`WLK{Y&1=0E^(v|7Y-Ig2wX# zz%~=yd=)kS5?}a${hiDNTQyhvF9kmfN=ET?POOar2N%K#2S@YYBMgU74*?wgAhdu} z78IM}e^%lDuMTL10Pvxpt}_c9%g0U#;2a5|ds=w%A|Q4D$71`>#-n&zc7`Cp!JVW1 zzhyk@`A-=XKzlZn12WzRfEUU{5ucW%0DBIU5aFyJ0#FA-@t>BYB9M`Ric@~6l&Hv+H^Mj+3A18n)AkJCQjoDWXhqH_cQs~3R~_?i!PmG>DUko^O+ zFbY6RkO&bFDgX;!q(lUK3qZ9HHzLpjra*p)0FXj(x4KgRWS;1s0EB}?5GX(d@|+L} z0ohux{ZW31|J{DhdH}Ns6o2gmG@e+O2qb|-m@PsA#9WaGfrBD&EDhQL(qb@IeH1Wy zBIY8H`$Vo&U>zhv7%nn!ybP*em4NC43Ba}lOg-~P2Hwgd69OOe!HNfUk^j45`)J@0 z6hSC50Pa$dkA;B86WgnR&!yl_tTliPbj5+rKMFu+x-xK*7MufOWnj6JoG3tW8Q5cI zAz1jH!LVA1w$x5)< zjxj*!6FXvo;U|if1J9~JYJUb)pC~v9#DhfG_=5td)}s&tdsX0QS{$GN;_PMj)n#p&w^^Z2Cx$;G-xS* zL8I!)XkkVJXwE?E=*iO7Km)qT(f)V;c4*N4dn{sf0OLlm)ErwNA0)yLXEZ?kB`CgY z1l!;p0Wdd#sT+xCfT}T=`k%S1;P8JHagYmCH-S^fqW}%~<%C8Apf`iHR*+%<0?ptd zN?9H7X$EVcw+6bNNa7B_w}9LW2c$tFSjJ%h69yQBKz0l0CR>aF)VhM!U=uhP$UYeV z-B|Ew2mK7X4)p@+Ph=hi5}!Cd3H*5C@j5`#3UX=-FnprqEs*oX{YPL8BtkDa6ku!v zNAo2;6tD#&5Iqr~q777Z$p8mWbT9$$c;^G$fJ6w5hXRL&;8bjG z1)FEl0Zw`56d<4jlqgpLeIOBJMDe|45r1LXi(FtWB|IcYNJRDpEcqWeX zKL;}~mfi{!bwcSFpU%~Og9?fNpTe;n3g_bD&f(%_`3|V*gp#oSpS3@Dzy(k1|Lf!1 y35Bz8Hdl9Zc6rBP>f!?6#6$7P|Fe7p%dP{@7yj?s$$A0PE+`#xS05BC<$nQAWid1W delta 18579 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 F{u|A&n>YXf diff --git a/docs/images/Subdomains.png b/docs/images/Subdomains.png index 66a97cd65be4c5bb844eed4465b2624a3595f862..3af5bd0d239a02aa272b58d4f04398cc05ed1507 100644 GIT binary patch literal 112410 zcmdqIg`Y~rn|dA8f4Sm-CZJ$2uMpycXu~Phm_LYARt`^sdQ|l^S5}O_dVx) z|H5}&c%h3gs!D=D4GC!X=Fk5W#aY4dJqUz3@caur;#6S;0zH)~%1G*XnH)Y~ zc;PSQ?Iirk${Vxsc>DD6L7ZE--P1#4HP2Z6_O#N^mzJUI%Y{kbF)%v5yEgoTec7awwgsU zyRJajp8d6rZH=GC#y^d9jddUHL@v&?cXZsl@`ZM;M0@_8Rhn|54?zF@QladHWSSF) z|NAwDW&q;;pPzYy(aiqy19M^q4m0q?fBqBtz%~L~AjyA!mf0DI0R8U;(Nl=(LDv7h zp!peuu=js1sABT}lMmwz8hcu}9^3!+tJ(L8V+7OB)5A{c&|H{5{3Vv|x1L{Gto^pd zPgi6Ea9iRg5fr=D@*chpfA+;A9e}e?3;WM$#iAsFx*r#@%6q>Za^A<#?qPpCk&R57lFK}*yB*KSw1%0-ln;f zBqH&rvFs1J8FjIpf?_X<8>zpt>T|+0&@%u18nMxR(+ff?r5?mZd-iHF;Ibc6>)UTUZ<@9yXpbAJl) z$`W948$mPSPl`F=`~kLAJ8wykBAODOyeN*KOBQVh7!5RX<2`t1$uif3iP>8b1vx%- zX~E1VJR4oe>RiU=_DSj-D?=%Rx&>ql!mppku2Lva1Cn;~I_8X)CZMr9|NFGCS48`2 zbQ9DYa#z0Z)i54+vnsFF$|OsudlghjaeCipA9Vx>&NNf(r@qL=uW*fM3%R;iv6DT* zyuO189ZDFd#y#a>wic6( zxe;8-=xtc;vELQDJ;f(KoyQE$yp4?w{xx52a!mc9it6ePpNp#H^ysIHp2wwZ-gjd6 zr_wllz0=d!QwyuvO{8%F_agy=yq=z(G0-iM|5jBd$QC+Ttl5Mj!*#=ADRsOwt||`- zxZ-##CZe9hb2mceyT6Iz?F{u~`h6mC@w@C^SaMwNeG-hgO*`*Hlv7Jh0IZ5_=`ibB zBRWw1dYMFXEmy zA$@9mm8){aV9BePHJ|SRY3{42I!E#WR6uA8RYMPpbI(qaLp_UVHpE(tYmd9AD+Iqh z4rwPoay35my!#~B02-!B`?JO`FV3(7wWQ^D{Bga36;PpBkKg8Tv*q4);0ek)s}^jkWgs- zyLa#G?O!Y~R#sGCE5QhdhK8g}fA#PDjdEzIsHwq!>dCvk^>Jaal=-OXl14JKut1NQ zn)~OE_5Z!$8}?I`!DgfNwy38y$Tz**GeW~(d5nrofx66@L2U50C}xv=x<~Q{^8|WL zKZx6t&rVqWDF)#DhG(olJxU{b+R1J9IfUV3BaLY{p7PV&Erl?#13d3xy4q@7-z%;E33`t`v=x&{kMtN3dUgO7)& z;_lr`P_(gQf7HA*=YJ5vgN*S@X}>O9DD7DimbW(!-@_R!#lRZs^jq(bhAE=XSq);p7y7 z)ZCnP@96PLWyX7|Uvz3yZ*jM3yR!yoYaf`3NKRcgz|q`m{^WjqNj>k>F}V?RJu;OE z5&V#{CqxIyY{cJ}QVFrGjLwdi5SF-A8e>7Iw-1k_cxlq9Am4j2N>@={NKqgE&|qY2 zOrdsr`Q6Eqf>R6bYfisjZt8Zmo6QKCnb7(-|4Z(+rcRO7EKK*?b6XMA3yoOWe{EC7zvao;LTNvl!ERsXyq6siT~OhyxG<_nl-5 zdmEcV>Z3vyInofNM|yuGq2MV@d^&aTe2;|Y$y(3Tg1%|m65G#C{8*yiDzC*KjXq5< zBa)bZyLTocuR^+`(bR5cJiL6#a*61962 zoFD-W(Qj%4QE@V1oiJiuLzLPyD_3d>q#_a@hE>xcJ@3%ean;D)?Z3oH_^EHpw?@=e zQx8vAvZW>Sv^2)m9)x*)ICvDb?2}C%V#?X9+G-DPYT=x1y`H#VN}2mIxK8tS%{NG(|^ZP>|sxRX?{L6Ep1_GDF!3Fh;KMC zk_6dISAt=_g%8>6oPte7~BL6f}U!dMXYq{vKKIZeWt4WPbvcx$Eixfn^7a2nki0kfl1` z!XybJ2`;Vi7{}HezHe89_DfE4nIMB;6XjN8BFidz?o6 zUi_3GkQ#Hsf2w2)XSA`LPseJ7h=8E4u^cmnr-dDZVelnK_)H;RWbYN^^avjx-z;o+ zv#PSP5Az3p@EE^_T_JYRF*_R@dYIIHjrH|QjmAg`+(JZ{p``h&h+Jb)9e&~q4O>d; zY-eQ2I1nNaU6Sk^bt!$|Uf?n~!)<#MM^Hx+$N|Y!lBK^>!YAvuwl6KbVgErS7b@tW zxoBZ#$)7ynp&A|DdV33QRyvdmeTN@_?m>*G)^JE#q319^&9`3r);0d`URMr!VU^q` zCnrChQauj56~8kq@!h*lTW9SRqfN{OnnrTowMp-v$(#krguY7cavRpr-6%Z9%^ZoV zi;JVRo`T1h@`>rwu-|&43e0*aHd8IXe3bJyfyR>IieJJS2*#|)hjm^3idp)Mhyc|X z8Ho=Nh$Z+3K3#aAHRdKjzJ*HB7?5qx520Snl!E=;+RZr8ErsSM4ICUa4Ga{HE%uFL zYY$nif1u$)OPpL>IA^(0DQv_WBxy+FXeZwh@*h$7Yk-w7i-zw6G>~`@eX-+d0{S-Eoy3%YuGh;U`($rrw*r~_rF>eqLd1r1 zrbw^Fd)HPt=$73-hm{GJRlmIu^nl8mzWs93JCP;ad{(MCPtG5r3aJmKrhWgLHvdqB z+^_}O*kE7wwDfU{>m@=dZi$n*ISnqsJlibU7X>bZ@4>Ed!XWPBf0T*}!}R(Dl%mkJ zQj~i_yTWvTiP6J?IGhuX)RUA1Qzquy&PTnuw6wH~iwnNkT3#{=3UM9YP8Xb#lZ!H< zkBnE57_n$U=j*Ex3S&otiMW<( z4zZ`?Y)fHbUpPAngEU)9|$Ce{v)ezKUWg2R3AP26X`KDM#S0Z1QL?OqrU1MlcFA(2D&m*tR*K&qrZK zs6i%`x?7}C(cPV&!^4ffMPKN<>gZ1M^5x6$>pmp`ay5evq#2 z__t#1)R0*1pjO!^Jq-;|5o;+2EzbFk?}{6Rd{NZr7ZCItGuj2?-(@kuN?LQd;H}>AFvu8gtd>hho$62$Ql%tmm{5378e%-1?@v@ z$m%w4B<^@`brgX60oo=ff1A8TG(`LBW8^sL3&avIkiVY&&rjGgJ2TVgXcBIo8JiTp zmSedjqvJ~bdDVi>R#H3f?vL?P!}gl?r>`Fo|0J%izDf-&EiHw;p&FOB@2^A7fmbr} z--`YIoT0Hf)H7&*s|yQzPp+DBG62_wySN++Q}Z(HyvNr?lnEY%TGVa2AaP{dp@&Vwa#4!N5?-n0zhQB zw*D-C0gf%!-qh7{dOEikrt;Y_Pr!r`(h!tHN=9ag{vA%yG&LM0!h&Ih;}XYQyU!fv z`oGsgW@B7dXY&DKF!84Gud~%pDcx@C-uVx;mLK$^dygtCh?Urg(H9pNvyY=E)KAS| zI$%ASRLHn0eg+eN?K=Wp6O;I2LwMrhFVF2_m^pEmot2f9jZFrObr&Ve0W^HW#4S&+ zt>RdUp5&Y)R)Kir7f4DqMMXu$#eg_#Af7_>1CWFZAGK)r^C*mU1G6`l{~6j!u$9&%N*;sWimP*=(^w34>U+Qfda++QZ}Xg^+ic-?c~XowD;y- zRK@qb-QB{Ksv1hn5CHk||545!b8~arMn+`lh=%7trWj1xySd$KESwI@azFf}9Y>ct zNPpuip}<>VYQwiH)N3I?lh_S})=O;czJ2?eorT5D(UDM7FZk>Fe{V6CqPLM|CZ>3G z=dZ4g4i67c{q7s$@CN)j;8B6pms6+04`P$pE5sRiE#&6rCUINbs>0w0mf;Ybb@-qs zKm=4l=Rd9-8$7++n>Y0M922CtU3x7u03e%#@Hu!`<~exkBe~MXrUsT0bYcP(6N$?% zCH;HE$uW0~610Cf8QQykP?)rmHj{v9`2~r_o1&PYe%K>%N<4vhnK`p{etdX5qbRu1 zVo$Hr>+-LB!dvALtdD+yJ312sJt2y29L z>JpAWRaGgK{}_DV>&Rp!EF>h88YhvSk&zJtAa3qGL2rrEj_75h+YN-|W^7bO30woD zK}v)|M`&IbVB875{qqrRfQ%^a$k;Y-OaG0PGW3olZMNUi(Okjw2ZEyM1DT{|%+Ev5 zdnG_3y9vGEj_nvV1*3OW-8#{8G-r^S3~Xt2kx}m=FO?tnqI>veCEeplPcs zO`Lk7mX0u&2PRQ8R8%b`(?b$uc;|$+TGQe8e}Dvf3tX%7aRG08k6^<{qU_VkWmCGR zPbsMTypPeeT1X`?w5O?5cYk9lKjAz_0t-Ff@7;jC+-9;z}cQULHqS?0)4d$xXMkL?G93NAgff5S_Qlt2q2{xFDs=zDk| z^F-wGQP;D`_=LSGJq7DH zh$0vA*B!q6^x3DIzNxT3p2ZvX&Yhk^Nh`TqAOmMF=0UnhLsK(p&bQXV!GWHhp0*d7 zABfa9i@Kf=G5lRvymkIb@e0ocp0@4neD_s0fTe72N}rV)69rD~5PeZEZMaBRxIqdN@sa`G{{j1CN+)pQVQN z_4J}a175*E8NmG9P)G{=@N6zY+?XYGbyx^ZYbWK7n!sIIzyU`8mKkvJTM<>^{4cZj zP6#zI&Gx$%VQ@4!&NP#glT4_Z)Wrre8=1dV#7PuP_pXQ1BAFsIU%eXF-Hsgrl33-I z(C$*}iIG+*Ds*sC4y3I?SI-;{R2rU-j}H((B_*Zkm>8fZ02Mlj2oR`Y7?3`kOIugZ zjE@=TH@YMGrDRG>V2)%%WIv8<$+sP8m#n_!uNMu>G1NqH8TG)83K)BPw^_>n2o&RB zVsbwG&8cF&T?azkP7bqsxcpPl?CJk6kW}$1gH-}XFhwwMQ=|c9AXS7tuFo>#4SfrI zu%~4F+Gse3z&Na$3piKXMiH`#ZsIV81tKzLej2|9?vgr~oNi+3beEdPJ?;;1ChUbb zdthJhdS+_b3%ycl0c9}yd`JMy=#D0&4TZ-NtC4~1x1IpGM?YII5HFQ>iF2u2DUdW` z4ATy0qhFNekT;)30*zpjmQ?BL^mJhH;>lI{LIH9_Kc{q2;$P9~e?H>oWgyA0vYWem z)^bJ^fOumBP2AdeBYd)p>~4?5Uzc)BV)MnAASR*1vFPv+9_tsjKZl3qY%CM?o(x>? zZ+*pD++;QZKw*5Q9G{S+TB@p|@4LzptjB<^xjdl~a0PTYOaR3S{p+YI3Bl@cH?K@h zpLPNscM2ZQ%b(V<9@mk?uY;D&(C>+Gh44{jjk%VnkBNv5effC@N}d^q3#$$z#thmX z8hGLnphd)3P#60-jf?xSe+q<6_9pjDh zG#$-6tWl$l;YCnOI7+BXoc#Q{BBN)37H()5wMl$Sn1kEyfs;Vdzfq6ef%M~*u5Q|Y zul%W~t*uRj&m{m#ye%dOT-n58)63ZEXFt$JX%AU|JP-CE0y_Z?Fv1=hLUmF%DK6o3 z;>h;3`<_NBP^h*T$3sxZXU-r%A^15>4FS+D=v!K&u$Up^c+)f(Tv6rKE7@Mw!qd`W zJzid3L}imNQH!14e;;qepLQGpf(sxQAYD{3hf|(OQG}GV(T%SdR(y2QKJMLS1T^COP)0;Ez;$3E zO*1n9hedaK%`>;gUMvEJuV=Mpk4N$$D0P;UlpLPrCyfKF=hQ7ZRF?`<{41T6qh&c0 zFNWs=f&vA^o&GVSF8+sUx8>KzIa+f0af(b4q~A(F%B-DS-S{5c5e@;xU^V=IDP*dC zVAaUas6gewiRzk~P{JXnVK|4xt(MMCky=-B;g7o`ROW~R&R)u|wG0ijfZW;vvKErr zK(==#>piBL2QcQdpq1U->mrsDL4SIUiQo~-ano#Rbg7Hv#qVn3ULam<}ig53Mst(CjU9# zHDa0MPiC77cjw2v%eD`&W`6(2?qzH^+*kRxF>9-9XKHoJy{&j2HvpgAX57B+@-JEa z5N|qWbv5%3fJ2#ltE;N0{fb~*>D{2>44rD`QbEB`WhEtY3mSYPB4PPB?%2J0&>~)M ziE_)s?%}to^CZHbjZZu}2P-{2uIr2^zf8NgzUKt2{<;CDvOLmWIE+(Ryu~i>g?aLL zY+e(*TPxg}(_1BJzcLsRU)Sg>$EtdI^CK*GG{d+Xs61~3{Ab^w$lrKLM8NouHTN%E ze6W16(whAr`hFK@cqUy|R5!-YI0FNH+PL6w7%dP?UbJnSt5Q>oYMpZYR8CV9_r&ng zXs3;unHFYGgQtCzi)#?4E4rOKh0WAdE2L^#mP8Tn=1->-P#XW2n6|mORk)D=l#$!` z$BYdRIi~bHLP#H5`L=_-eW(ydm{j`Z>ZC&o=QC%?_a*9ueUwP)k0r|G=6Cepx?x~e zYQpbzyB{ZVJm4ul27zPUzi6J1NN;T`TIVLLmX0)7iU5ZWB?MCl`~!x*--P{j<(fIN zZgzu_0jWBcolS~AqTy!PyhlMyj0_JU6!cF?!i#`T4FQ$tG@x1-X0l-R1yojxJk0SK zE-&7q`_;v5{IJ^PgAmo;nfsr@c;ND}7(a*)$pDG&wuHw&@+qp~e*HFSe|Im0Hu0TG zYB%VO5GDa;gzU13y$AApfEWF(2ThfN$|(D@5prw-pilu1AcR_1TOCAB${t@-rpz+{ zx{3}5jMjkml6xq^)_)`OADu3t7W6tanw6b>^4B}1c+*IAM*QPh8gg{RCK8`lNWdv= z!?6Xvrwhd63(~vsvU%-(BZczrQ-lj-#wPWjSfgi>A=up|ivV_z^=$_XUWQs;Vek9m zB13UwpbzHGI4lR9m<$60Bk^~u_UoKBOmhQXjU|8-QHCKnJHkUFso>EVMZG^2=$(I? z+RhCM3esR2d}#`_8$=cIIvgGd7ooZ4y(~1jIqzWSu z3~=^l@yA@9ITQ^I4T0JRicNk)vi@_3nR(Em3bUjw64SDSxR5gUx!pOC#tJl+NOs6w zf1Qbf0aooVBz6>QCjIy3S}{439B=?|&=D&+u(1vS@tLyozliIIp8Zkc^(4?!FU64* z){==0a5Ptf&L~Pdjohet66rH4~bIalf=FOMYXkA#RQllmd>b|~%6w>$RE3K%X(DgZjj zD+3^a=$47VYz8wz^96i}Ex@`vH3dwPlV_78d3I?@et^9ge}hmGxIc8x3zGwY z5dga5S)SwL!Kqu@pD5wfBQpD`>QqrsP>2;^t$yPc&7tJd_qG06?TAYkoD+@GsFZOKjF$L0uGc6xlMxF>{AM1bOw13 z;Ur7y`_meLU@9pq$F+{P!9i0PfrguD3RfHRY%UZ<@>WaZJU4HQGLHPr1K8JiQ{(We zoo|3#2))tv|q&{TVW_!v6gViP)C0#`1W=@SJAPkuC5M{ zUS(?iwkiD^(EKPM9m@fZwk-^JW}`68R@wv4t%`)_A}^gd_r!8~4G*?&x139P?c_6) zdXrc*tsz82NT{c)oAsS2ioB;tgGszYK=OX>v*MdKZ>k88ss9YNFtgFZ<`W%)Vn&en z0OnBR7C*bSSUa?lo(W5)&P*a>dv|v4VMQ`cNG(q#piHoNo}u z#rJn($W4KaPPxkb_0T5+MkkSY0{o(;Ns*(yJ-{&Dx7<58_*!lMzS#Xaot_EX1hKV; zwVfk*4bKP)-JX$wL8R$ik32c{-pa|p%QGH$(w2B52~o(YwiKGyjx4Pn7Cs zmzNRHMxvc#FY5BICo=4Ep7{;xGF1+Xse-75zooMJeNvP8|F|TrAhu^uLiwQ}(t%Se zKgr|zW+ecB;~vx|c;OXv{`)cleL%qso9=C3Cpsecxgos_su}S)KHOH01rXjLVwr{6 z%Zd45mE?RyO--C3iBP&S!1)_hVR78~{|a@L8O5#lZyUziytognCSQxv$ETMKwM*<9ea zQ>s_9b-kZPjNdEw@)w!J?c)4AHNU#R9~uBSFkKO6(|cu3G??&-q{5Q?7LbuxWn(3bIZC$S!n%v)l{UWh@(xR<0tzt=BIjsPw$nqmu;e>n~e zhQg@{n)BwBCnF}GdmY=BY((YbW^`FVXdV&tnrxBG)j5VGjU*ff0=V>xBeSQh<?;KgCF z3n4(#5m;%X$5MC4L;>l9A0z{0dU3lgF-TTc_CjQ|6nvUDUL}OmMMtbTI=CEZA%cju zE;y8_GHIdRS)Q>fPNw6>Fg|0ovoP2MLd<=~jc$pZoT1^!Af%skB%zjKQ!=20fnum| zCy*{dEs+uZI@KaELrbNRS*wt)~%}^_Pd6n+Epa+p1+ME;;+f(#}qD;g}x5D8LVw^AzoiH+`QK7Y~@( zL_tQ0=YTuP0+jKtbT6|RoP==`fDHgDpOd|t?g()i>KY?{b|eKhPW6+o^`>F8>E|y} zDS39D+2&=69h4ocB5d|V5tk>RGqlR@%yd>an`ZNwQq23* zF03ppxQng4z@m?yz~xvg33N)W9FCR+Gcwu?8hKDuHrgun+5l0l{`$Qll2fOzY`Sue zMB|^b(Uekg@)z%99K-?11<1yT|Cm0pmFqCIuBB*#7cBVG3b~E0gKmivr{+>--?>%V zM|$fYXz8W_MgQPV;a3V+}>=~ z`FnkW-djf1Rst+50HpSSy###+nFrXkfM7(egp%p@5St%Kc#=dcjZxy6?pt&ND>|uM zKP8^qwDlf*^+j_5^0Gq2%%6*fm!Kt%sZ<3J-NBq~C^~)a;+MYMA!F%-%}o4|59QJ{ zO{K)aopPU1)Fs4`(nA#JIy|IIMEzicuM>b-!E9c2I_H$k!D32)zT~WakGDg*#Ch^o z23f!xIlyr5_Q6qz06atA)pVZ=l407jS0L%Vy=p_LkD6P$yHTI~?G9Ko(BYv`iWk(b zoV0;>`I@xfg57mN2L-9kcJTE|ouIOqB0AqM`xij=!ielqtIi2D0WBtqt)Ez_^>{rLi<&w2TL|%D`7$A#J}o^fILj_fO#f#ObKdt> zP(!t`HVLEUKP-r)aRcq}`b+BY@bH^}izlC^O-kib4tx$n$l>)Qq7~^%Cqw5u@}M`+ zt(Rx|pcI`sO;6lCoKk|gkxbwG=Z=Qv7ea-Oeq}GWB7zWe8OU&2OS>ZToYW=QUk68i0Y&}-rgR!i+o602$6?v7R!``AT}bR? zD>>#9^Z^LaD1N$C6&96+1f@Y`nYeLYKqLzB<%gb;*=(kum|Y~7fELAV`)jGN;!;G0 zeL6wIdKbqw*%p)RZ_yj~s+PCq1V0xL4=+g2_N(-H;v>T=IZ5Wp9<{uprly$%%>C^* z6(-0AEm~Z*r*{vqKct_ivfz#a#W!tQwaKw1NXDi6EQY;@0vsiC0_BX~!8>%MZ zRA@E{0aM)=IgDX6vuI!$kvHE!0#$o>@Xo7YVC?V6KaI#1PYA0d!&v9ftQ6y;dc#PV9J=t3McWO z)<`Zg3NV{r=h#d2aW#3}&6h@@I{{|3; z0P4(o-qWh~dyFV9ghqUo2;Tnlg;|ocw?q1Ne!eMok)6A%Qcw%wx2rvRCZt>5H# z7S2bMFgA#PQ7ogVZ_-Kl4EK0;ezjISH}kl!JHuSRo*b(ZQWBsw`+2!aTyeIUDzEg$ ze@}Ab1Y>e%>*^bNs>i5elKgwMaVRMy2!!s&j9Veh#O8HjIisIUTHi$QM{GMzKvIRH zPmeH2W<6d3L9(-wC`4RI5Q%42;g>ZRGm|>$NmUxez!JHNOI>ED=-4tzHeR@R3)oro z*8bg+ph3U4zu*5JPG}(T4QLxFk0?R(t^+e-U`&g7QxiV&VHV)pK$<+y?H%569 z6LEj4B`f?=sJPiX>$jxY%Q@vWcJkr$aZLa;NIuk6q-V{^(PHZT>2Zy737wIIM|23? z-DVZ)aL{%oC|oVXUYjE4HgQ&YcPtb!d^B`|ZUc#;;#PzgLVW13&wvwit)Fl&#TQgo z3m0N}X0yB@Z1jpZF2WW`2trt~_%Au$jeYy{INri{A9~rRTX|;#51kgTY`^I7Ym%hc zAfZ+uqSHMV-r>+bSqr@@M_^N4?tmVi&DEO``9l_8~5I*>LXc*-}4~w*k|+4GHm_(XSjXIEwZI_k;BjOb_?*~}qODe6UPIYA^yo|M1oE8>k zPsBY9Yus8wP1Lu#K_LUL;55GTrMUhji$AcP4b;M8(NiS29>B!3&Eqn%CCb;qg zD$R`Ofj(q&uhW>qGTox5%Z?OAXW@jjv%vI8@U7v52!ST&CNK07=X-LDk@vP&97__W zbgrfhJ=hzW2R9$lo+H=TL5%&fHLQ~_u7Z!&@plYp?4QR)PhOB;$Jo4NcU2hYPjloA zMfCpdB@D}mCZ;H5C*XJ~r|SQ7e1+zJwjdl~dXwF~m7zf(u$>#(m8bB6RvTH%+V{6q zTKI~rujvAR=TpDwN}hj3Z8BID46jt!-EG1f z>_WT8I-nQVAtzMqk_V791Iy3qQl;_=q8w>G;h#ReykSZ%(yTgL<$)l!NWiGGT4g(z z>5?*L@y`*ULHscty_V&o^j=DO+ZEO*W)Px+i8jWcov^Ey?|gMw{ya#YXa~T4KH1IV zv0?=ym44iZLr31D)HNTQzO%o z5~7&L_BXFuzJd&M9>7DSvk7IXShK(V{R$l2GCr8&*C`ZYf;)}ivgn|uIGZ0FVI#1Vd3&_Utf z_BT=p8cI%-Ke+!LdibxrA8vJE?;)M6PLOvOh^ZhJAR2;GUp=onpDawdxeiKvM7Dyh zO9T5Zzq%tW*g#I{?O4lL(OCHK3i@Mby5S9^guN;+C2ga*2t3fy*FFR4li^#Ezp;(x zt%n;vLk;Rl(z*~anoWFN3b_CKdPPxG+lOil+%R>&5Y@0lDMAMk9?4)+YHi%5>vQ{@x3TU|u7_jcRk`S=kndfEe5wyKq{b+%q*g zZU9s1hDoRvXp5Sb1!W)TX4Ak@p~FI*J+dA#X;+ z_Yo3FuD_aoX|PIKvJyugSH1*^z|J>(Yc(}| zkg;TpB2+V8m+_VqEvXJeLI=MJff)r`TC)?(#&# z%MG$$5;e4ZL{-xr8TaoTHIUIEZ*^TovXGM*Qt;~QEo>$DFi5e;mW*PyU5sD%{_C0U zCZ%jDiGp!<>3)n1ZDYF()nt@LB(-Z_!6Q|d`ooZ!rI&}blDz_6s^n}+>~_%;vpH)w zL`3n+?Bb}AGfpdSg@$$s2Pd!)S(VQL8KW8DNEXtM{Eo;Kk@_Y=biH(|ssG{>Iuq*v zIFR-Th%Z&_G7rbXfo+*#>fuT46(v0429DB99Rzc(G~$FR_tK!WPpx6I*bOcU6xHn< zCGkp&YXnFcs=>U+8hJ_SOq2o|ps!1zcS_f3L*#==Z#rrl|i6aX)0Rpl~&dCE+tUoBi+r^~0(YpJgH@`djb zkSOQk+)e`_0r%f~ZpfA;2d#r%Ny9s<c`{i}r7WrxHHO6UEU1ylmZXY5ifX@|ZLs0}PY$aQvlNo}d=oaoE^lp5TR zHey~?rLxabU0Azio!P=IoY)aIT6*^*eWK;S)vu^>^9~cjef8J= z?b|Y7MG6?T_}+dEkcC;tZH8R2cKr@bnnK_*o*5{2$6zi{$CQkUO~B!Rn1;F&3A)&8 z-(Op8Utiq#%2jO>&q=y|spy}=Ph^8_7^HsWLI-A$259KW%Zf~|x4Q7qw+PLcs4)gq zc#0|WqVRF$J}0suzi|o!76fqI@0eOqaPEG7BI71(A;m?Ny2v&cqyf|>>scqRYJ}Dy z10NttnP=z8W&-DC#1f@x$93G`p}hkowPVxX$t!`K#<>#uTf9sm1(g=M?xu_fIB;<< zE+gYKc<0tcfsJ7JQ+qd^=DbBk<-K4X_G--+*CuZJ4!p%gT*N1l*qWT=6+xxvL+l1W zH{F1RlIIaMHKG=RTAb&I`(_)2KG^c$4YKd!^=;yZ_XH^aypT}6J`n>$*OIIx^yOPW z2)tls`#q~gzPMkOtYu*L*;GoW7R^N=394Hfqau$1S%9lqT|6V@!89Q7YmirHA=62W zj4sloBXyRzkuM|-r~)k#hiU!T|H0+RcuA^!L8Uyr?!G=Q*|@buL)Vn%PL3;reREqQ z;g_!?S|TP|BMs2&5XLAgj^>}gUJ4nK(C}V5J^gX=U54&l=XHAp^J$7ef69k4mQ`{} zaw#*+EWC|}FVsWduzq8K=%r^70@o|uB!7#`k9Y@c7!N<(=nJFl6~P+3!m&&Sfmj6| z0}fS`l#x8q5jxnKE~E0bI4vyGL7%bWIf^aTU!UQVVkgwTXg`H_2HVBKm5@#;SI|zn zugjetOK|mB3&F6+_ciIL!Q1I&)?mp7cEuKlP7WoftNR&oVE`b+JO3WIQq$9I3Mwnm zM!z4NRq52z;h@W+BW)HwO03yL*~1nhLBWk8c{_-TS?aCF5bT6>gFa=^^hgfuFOX3T zt+*sG3&(Nm!J$nShtm)SeS@cmcz(200k-Kax$3(T)%yxvzEF&9bJvTRKlWdz+Y(o1 z*BAu4M^Aw8e?diuf^flf%d`ct_@&fubcIx$SG7N;FNEw8PoGipV!j_$j$U#R8`a(g z-jvxcy7on^!(!~`1-%Ag+T`V$fq35%oY3KniNr+`eHer);{NMAD(_ycanI9~QXtm^R&>;|fH7{13H2|mGTgKtDQsEW$!35Y1QjOl2v_F#JC+jSqHy76`l2)b&dKh$woiooIWziM-FYw63p@7 z=xEfu!PsOee?9nz7}}?wCq(z#)t;1I=juz?Ow8wx)z+B z51Wi$CT`S4;KINmh^U#K`2i85zo|iP_xzEBJo1?A=T6+MEwt+Nbm_Dsa~td|a33Q3 zbUE^koxq%7i8{+rb9@=Zg^{Whc%;Ih49w{mKxFuO5Y$(3U0VCv3Jkx} z7F4;aNUcE5B&`HRTckU&U${d!+q~m3EgzugH*$vk>CqB_Teyo$177cB4%zxS6)JfVd~)^ts?@HA z-0{3yp%(RF62a?-y?9{7U6w6HJJELQBOcf@xEP)aUo6+^H}XIUkh3Z57!AzvX7!C< z##<3mvlUvVTdI0wAzPq7mBUxGd1{FPc16m21$%_zf`%5%oKrV1~V&_6$D&GJav9N%kSQK zI*98N)1{B@vK&=ctSweT^M=pWm!X%hUBuCBX+aILB4}_KjpkOZB^2YiM>DnP{{hNM znCgR>4r=qI9SR4J_VFX1dWz5=+G=p;C5Kuw$s|Q&h&}hUa!4m1o07DUs!Rqy51}T?mH4?beiHiltcX7b#ch9|tBI z-T|w%#4EU+oSCbdQGnn9m48j+m164y&(cumah5(}DdVM+w3QLM$A)cWj6$ED-V zbFHetW%qP1Z+x{Fl^?GYo&7!*d^ zd6Qh%MMg8f&zyn2Ee+SaUb;EfgMpYP4=Hn0SY7-|!Hxa5zX!s!9Nr%^w>bspv9=`Y;D8i1|oef!wSFB@! z7f)0;_x6iS-ZuuyaFux#&i&9Y&Tf+F%#gldt32W^{en@8HVJG-yVV$Eb~1fUF`OXz z_{)jwG_utY$&##TMsv3q*fR$#L)p~0Q9Cw?G>d&ARfv{-{6Q8+j&ennL5N8At0UQU zOBsg3=C)oTF<&dvZPxT09?{D;M!1{b9uL2rPCg!=JRS!;etWu6f817gmTM^Bg2VH` z)=UqYKrJ8Uo_BFmC$Gy9XjX*JBoErIV!%xZaw0@z>3B;_2Ru)#RJ>_wB`vVA2>$J- znmpAtJ3jBSrC_!_GZk8v)FjjHs8g+fK{o`)8T)Z#tMT(CDPNF)DLM4m1uSSUIJQj# z5Zj51>n}^N>Z0amy+wn)K)?AqI3)vn1syboq$9~h#>fid(fHWh)M?+eDux1nCrTd1 zc!o*8g=6`AxsIV0m|mLiCH4D{`*06J!kx#!rd%W_g)WgrWDYDq!lbBqy+04^w*l0! z1hMoLuqay)L1HF?6wB9$W1c_?Ypr6>P5BZM=uLB}k9y zQ6M!by91aX0?RXjV+iN_O}=}d%*@O{rrM2**XVFSH8Tgg#BRnJOUE*5e#avv;n8h3 zHEWBm5WvmOui}l>S~-90LhOr(<@kS0y=7QcZPztScb9ahY`UboyGy!LI;1IUPK%5@$y7Ieso3z6+83Xdc!Ow9@znkO%O&2djZ^Z)&G-mQA^({J-n zL9{a5ne@pkh9YhFf)C6<$z{jGH{chAMgTqN^?URwup4Oy`W|K3-zo*j+vHJVW$^wn z#!NV|4zN!`m*$?t#mxh5{bv+wvNFVYv{hLr`VZThwAC^Jsn{l@-u+e`L;u77!IO<+ zkV+E_ktltqKi8^XvUfWY%c!9}AfPN-ESHgua~TdMil9f;e@p|g&-Re)H8H4Yuvk!j zqbMmEHeL;N-HP4D&WaEjB0f|%3TU$He~*mNQ?NGbkaiFqhd4(-0MJG{OlAp*w+4pe zs2q;=V9X|0S;*6R;8j_u9s1>t{DY+t)J-stt2j-$=BnrdR7c(7_7Y8Yox&Oo=t8d5Ccl zP$FHUW$?8=L6qkZtG@NZ)YSH3t>mYP zcY&$JY^RV!CgDD0`HX*1p13`|MM)KijUEoTU(hZ-8YVdfD?zqJ@ctvZtkVw@t%(_E zlhEvtdpJ^*L1;;kG*OmQIU={* z$U%N+txh&@-^hq#im2CjgzZAt_>OAY^*ejK;TMi&KEUAsa8$?9Q_MSk|G_rQZ8UTy z_gt`i{m~z3Xo{$>d`r@OL4x&FgP}@UZfl_;b`?fCRd&Se9~7$?d0wzk22mz`kYrb8 z!?rHx)l3A)a@;w24T!}=`3h@7MPW8vX3ikou&?{~RKuX6*;16vv?o-%pXL+K*y{W` z9w*wqiC{ILZ9`dx4W^-^LU-Y<5*=g}jgSshq9XN__G5%M$A_r=v5#~`1*9?RV}keN z7Y=ZEpMhF0sjj}2hnztwHc{yIp~@HOelEY)-FpD%2e(F&)X4i(Yu!!{Y$(Wqh)eKFD=|6*(+vBp!7Ypnhwjq zAOr*=ezmht!_C}+U#9uKn66ZXB_AUubQpWjbu4%Dv$6b$bi56!>G?+imAK1p$8;XxT&@bjp312Cq*0CkZVJS?uQy#4#Wnw~c zionf}9P86S<-1G9s(OO`N6-;AT6@B^FdQ3`C&Gk=sC4wtCR68r;$I>RV;2XJ0*}NV zO9TzFKO1yfHL!HmbMbK(ypZ54GQ5n|U6@4OZsZ%q<9*h@Za0cGFQTcy29;#AR9vmp z89B1{>t8*^U9QYEC34rBmhNr%u*Od8D_#PD^ z^%KUw-JDr|0M9i|n^%k^F+nvJ9nFI6ui^9AbtiN9s)E{(E!P&K%#4FVpG2f93?Pz% zT}LQcw~W}LxH5+*@Dup=_x#`ASbS!zRLDDZ#JbtL?UBh>7y8-b$y>QHiu0~0UH_iX z_%Y;T=L3af8EK z8cF;E<5fhjvS@R{bIuiCpUI20uiD{6MoFHOHo*|}2#RZ)Uo~dsTF?+KTt|HMm}ytM zzUeZb!c+$q{l@UyhtADE!r1td6I9T(q(@}thP?ZOzD0J)zSKBw%o{6m0a@n{pM!2w z>o#KkQ6u~r?mliVHEY~CX}|mT7?&7u$6s{uSoc=g^|518;@d8!T$Ef=&LGB%c~M?s z;TLJM&zf)sLj@EctNeeJBlWxPZqIxW6>0$?Zt`$j)aUd{3eu;SXirA$Y9;rrM(Dwx z_Cv#=l4(6~9dY&>^dWS6!uWqQ_pBW;ij-^Ry|IXt@V8o3Wmb`}zD%ERiUeYL&;?6E zBQ%utE|fWcetb#|lK56(Y)%?W4gELa_!WuyPYrp`KNj1zv(Ci-nn(j4?@<1Zu>A}A zSn|Y9ilIp>x5$t^13gOc-gE!&XN{IKzfP-}*#%T`=PZ;|)@0We+3YlnV4_ydEjBf+~w|60))JXB1^PC2K**pKU znNubP$P7j=qlUqHKzt~PvONS}io!YvY9KAAxx`D&{BRs|oC58W}+X1q6V<_&p3`D$7>BK(LoFi8GN2cu_Z1h{U}nmyf%+nh_#tA@uwUYSN5BNwEv#}b;ITz2^f~l zb(tx5XoCA)s%xy+Iz{F!8hW)wQ24>_Q8@YEAsg|ToJor1KZV+ zLlWvdl#>xI1`ggrdjn;XZXZ*!ls_=t>pqDil7PYbU+0u1MQI`aEBC`SPjsal&%Oo; zNtJz5dwX{24qZF5td9NA0f|_~tUL0pH0L_j9&dJ=pdt z>?plaT_M#!&CTBSyyDd<9%)+{*IWrQ;xL%{WqaSHSZ|C6S9TlS|EUifT^ej}*-^Z$ z*<;SWJBMiWpE!VuG(Dh>YI8&1;mHz_U$b z!rF|CFrb8GQl=6#BNV$BRZ=2D6n`IM#v9FObjPL!UmqxV3KTtv$K;rBFuu7#5{1rd zo|X@qB*o{=6}xFN=%c$`Q3b^TAS;yoZQl2L&ooFW7cyyADn(7ihY^EnR=rVi4JCOs zHY|7^;OII)e3BS6qvO}}>Sf>2=>q>j*9`i^RjsEmys7M(XL8MmB2C-cM_$Ms$AWCS z)3WCfvt^XsckF*kt&{r&L^?KklK>`LR<-)+@;7V%N;U1(Y(Qs_jRlfAt$avl z|MXPj*SAjps_Wni32gap&XL>sy;uOCkFV_UNO zH}wUZpl1WliUfs=!P|8DBj(7Pob1$iZx-t;AGPo5Q^uyMWlWK-@H_WpKW_TLG*2Dd zPR10F?D=L?$Jy4FF$Nv_xh&|h>`O;@WXNieUOpm*c^37JUqm@9BcW?16_xLoXCbN8 zRwG#g79(^PSq@?Oy4BLw?)|&%EQ&_+{}T~& zwZYyX1ssSSG9MluKAZr@mlwU>MS_XM4Ng`2N4%aQH909diBlo-9b~{4|BwFR13%k& zMB35V$%?lMGDPDSb9AqIUsHw-?1||0GwJtZxENmObR8fJj#uRbHyiqk;ZaRhf7O-} z5AI8HCJ4A|WGGj^sG9NsAA)8r6R>bBhTciYJxW;EKF$fCWSubOT4oM@NRgJaio?go zM|37t4fd)6Zfgx+Uqo%9g=OVNQXOaMad}TVyz4F5wY9ZrPD?+AcXm89$0|Tpi#%8m zx4+YC8k@u%|o0dbQKbgh7@zpWci1h3bF ziF_?S0}FzT)3G2|gW zN%%&g|93ueu+;0YVI1ElSAgf$#!r9M^CM5s#>^Y=P8kD1(!CSD-kE%sRSrT0>(+JB zRoHr{Tcr6B2K{6Dp_CPWxxr*E2I1%DHlID($Pt8PqGOX)*|AS0RSVJ>+9;AzP4cX6 zA4@YdJ+N|S!wC5QGHk;E1tZBy7u?tf@kH(dZtUR!utSeh+_dG69a99xJ>0y!5=>@6 z%I+(`v4qMgHEyl3k#n{8OOxyWhzR%iX1X)88^{67o!^GLimzErE`%C+R&MzAzjVU7 z7~~1x{RRJ@m3pW|l<{c(ok*mQyI}zhE?}<-sYO?TYy+=Gh!`fW%k8%arT!JPCe)@x zAl>xQ!dgSiPy4tkF2eH86mZf9$~#baCdf`Sp5G9x+RQQ6{ZivrrC&Tm$+ysXpZh-= z^U4KDVp`Yz!d=B)M#A!S6jpQTTUzu&c$e{e1+Q{*ewV^PQ8RQJ!%!{?BP?h>D65DL zx%b59uZ>>2)9z>ue{(xK|A1(>z(p232U91L?;M)}BH(L7h0%!_!dXivQ4h@Wk3t`y zJM#2o(Yn2&B8swBd0hXz=DSBrzxl@REWmcy>05%BMy2o^H@(CYwd_)Oa@h)PW;0xU zasYvD^^9XmT(jwLYJ9h;nUf>R|C(EKfUkKo;gSI9m+J@?4E)$*#}`qaJ}kfT zBZ1iQ7P8niOdg;eN;pO*>yR{GNNUf_A%Z;(WtDu^pOL50Y@Zg`dhtlvA@4I$Vo~S? zqY_1BN0<5O>7T0H6@=UVdQjg8kD`Lz{D-{bMEh@Z$@itR+NpmGHz8g3Yz7Xh8_xxd5n#AMBY%6!M)C`#X?4_S)?iSiH^O4gQB7$ znK3y@@AX4c{M^$mo$b2<{uoy#wOq#4qPxP!;>dpLA4NGM3=7f;3MdX@M>3E#FS6v6 zbgyA#+Tz2r`XsGVXwlvDA>Gq4>B-vm_eNw&+V)Ztm2ZduyBIuB0C~LVcltXjfVWX` zUEf-=#KjhMTwI*8$CG_<167$+%JAY0<&O;87nb~sig=%j`6MZ7I3B)zw`<98eRc!x zoHpPfB)J3iK(FcagApUud)JC0wc)p^g$)hZyq!pl z%`Gi}g>A~Lf*5Bqyt!tB$@?7`i84sRUh*HY4R8l_mrc1<_F8sSPpbEKe zg))}NZy&`Yil3swLi)|+jS2}iLO`+T-aKzG=pEmOlD_2Ze`cMfLi1~Ds1Vl0 z_1wd9dy;sTfQ>8fkX10T?FMus>|>x{I=*^P`j`ywmSb6y&=6u1fhhg$P${KY(y|X| zAjjWuV(qjwzDEC~YmMz(4FTSo%uGz!(DWD`bN9apn=*TWHGt2_>y!kR}HM zCh90Fqo2ZZ9z*fBdhgEr@jE<~gua!SGk_D{fi-VJtpH6Du>1@m6!1iU;h!wBD0;UC z7Qg@G$xP7Ef4IS=*Z*rBcE?$QC{OvXs&gfvfMZ+d0iM88@JV%Z^L*6@X4Y$TR=wLw z_rcG^WFL1xARSWkK6qDh!rtyBy^A`Zvvm2b7 z(S$7w(rEHOV4}v=*#cyqAFKaDGA<n*(25#ej9X_zfdq(6 z!bwN`0gRVyo?6$mAdaA<-idhB{Tpq=1g%3f$O$Mm>BH#BT5KC7I+}!%xv*mIva1m!@#(dOmKw`7 zTIqcCu`V~L16}Fi_4W162-NX^=*MVL6AZ|>N(hCdKfs9BqMbpe*+6~D;_3T)rmVy; zWdu3%JzL*a$aKlyh0F9`owghEV9c#`AeT?r1ly1V9x*3`lGkwLvAS(?CWNp%_bpDd z>XrVlIQg(ard{E_=^$#y(Qi|Ob9{jZs2ptrd^|`Wu3jqcxv)ir1%r=m(l^{1Ls@U( zkTTg(P0d8HAG@{zrQIZgXu^W3HnSD{_+$=!d5b_&m5?K*8F!G(B2E@0;-$irk{?Wp z1``uvFkqv}B9WT@^^=i2Ir#r(3T=CbQbjc#_w7Y!w||3jBEnu{D#uc*_xbkC0QHX; zmeYJZSTgB8Z1*Z_8|tW@-nF!Ib)&N{f3Vx`r2Yd8Bqq{G2`}ZqIFRf)AH*ZT2BS2jnL!Qvh(r=t4um;6J(Au5@=dX z_%tFU)7bd~8{XJ7X1@i7c3-|M8+~p{*>2k~#HFYNoxA!Foh`vVVTE7WTkt8n?k%HC zNBK~KZmM#00aENkm53X-^q-qV&xfv5U|wQ-hQ}dC#qWVi)3R|83T3v;ENmAC@ z$_wg9z20O9eEqiAUPNMlAAErb+lf%aMb!6-EdrmtVxeFChgTX8wggP!lz}=zO+=4T zvbC2&mnC;5?UnMF%}F?s$kN|dl&-IP|JxJ-rLhTMr&Avu*J`wU{@k~JwYz@dz#~_p zKxk{|Nf=WT&p#lJ@o&^=7>u2Kc#62gk>flM#th;a8Yt6H%6=1^gqa<&x&MUvEa? zQ+=+6=WN%j6`(ys>CvGJ5H_f84;u9}0Ot zNjzqxr?*dr5r2>YJ1SN|3JA0uF|8JGr~0)_sC()6w#B-ty3Gw~x^sk>A9cp>0?y5| zp$!@+*o=hjkW%^qH*?1{r%YTCBmFl3IX3U6Q{7yoXp38TzRfAC45t-_OdN^#SOR4* zqB#D-lbMk5Gd)}}(ibf7%VE{HbnTa3gY_4UK(z5m(v`34PIMt~?~>f2uM$~;EaMU& z@Wr)+R)t|Rnq_Nch_-}mnF*;kuCyLUmnir&Uj5E{amHzmw@#!gc?T>bzO9mic=7>T76hWrR#Qo- zQ+rxBm3|modfZ**!Bh<&%YnrIRp=^V9ay`RO|P%VtHMx3BJFdi6oAoL$UWsacsk;Z z{@Vr_F1qUKi->j4Vas~pZ4p0alpi64#(!eAYnCK;+qpsQkdu^f>9Bw|;uL6YPytN6gyFI*9DWC+Wk=RuDA>VmH z0k9H>{Jj_X*6Gv*_YY$bnfx{5LQ~%a1mFmI5}-api$n@lT<}QfTlHmor()nAumY!& zYoD83@LT)zJ8M-~rgmUB&ZvdWoFWwN|#yemMDiKmYAr`t%Wo{1OGz~~20$tGw?jXh@G#yGWa1797HDc}|(wQRn=0liE)e66-K zaF;Jrgb)GoYAm>?&8%f6WJCSfeJ6BV8w~HlD*g!*A?%lnUU;^llFH^in8rHy*-Elg z$$gI~r}ENpLHn@~4{VfS$Lw%XYCs;4$TLM;Ltq}w0(yPCD*@cs#FoI82GDJa^0}?y z2pE3>{rQ)V_4t$xRN2B{Jdq1IV$=tKbhh)Ht=rT0@2zt;zewM*1c(EZO8&sB6&M*} z@)|>Wm~6MMvRB#dh`;`tmx~w7|0jfvw{^7FHPkLXj%dPLF+e1$j&6(x0%m_~U%hNkx1Bx zhhYjt_$;lcYbZ?qZcu3u;WEE!d~$Utm-SKM4XDh|nQ06!H91wPVO{&H2N>lC46lH% zq2=jr(e|rZUhNdSD!&UILK`7^PUz^&hucC`hkNCaQ6b5b%|Y6@N=bm=7zIV zb-L#PIA}HUP_O|qt)B}G`J`_4=Gv%BTnsEiV3iA;Ce^RYu3`7qLcgV8f@j#Y&MZ(%wc!|umx znI9cS$ZNc%ih-g19q*0uPju^~SM!M*fuU;;*qE8e_J6U??k1OE?ET6b;dY;W-t`3e58Gf>5cl?6 zx|asg*NgITZee`l;AioJqYDSq+O5UleCT^Zb*&S;PwJcD+lUI}+Rq3|hM*0(S{lW1bW&wGxLmI&o ziU4}gt8~MmWXW=kQ9mc}&@P)A_(Xw3Pu-)3fNJ`uys~nOJD(M-dQq7eVSFP6+;M0d zqOE`zfXdjSa}H7b%Pcq%Jd8H@sUGnNp8z!1;YRRGW@IE=AWPlh^1c$91({03=i=XS z_g(j>2gb4eLQc&jMS~ED+<#?#wN5%=6HxuIt+&KbqEhfev`*H8w4PDo`8arz+h>;E z$(tAeq02xkX)jr@XV#f;7zD3wBn`O@Ei|oySKhGMqvgUt(K29HNVsyK9;y|&+ypr5 znt{a@MDOKc{)6%L3I%OHDi%5NH@k+SWq`3VUcxtc}ykKNsN55p$UlG?dJJd?1 zyclbkD4}o!IZK>;?(4Cwqa!djPU>?0NQpH|#s@Q&F8hsp@`h&N#bApvVPwQE$0nV? z^Pbrc7$jC^lVZF&$!j7|U*9`H5%4Erh4u6TIXMz@OFWUP%=N z_##7)wTgzr^M82~gckqdT{@9_pis#ODc}^ZIl{^CdqfCy?3Ce2&DRBL1G8m#m?ZC* z@@4Mlp8JCUKuKG$tAIAF3*GEu_Zq1A<|5_g~c>yJm*iitTq6Kl%$8oP1^nq#UFNkz6 zZrN1ok0wyGmZg%N%^LqO;?*_hIp+4z^GJ{MnslbYlH6Tg25rLPCphpM#X6zw3W@jIO?H4`c@;MxV_0&#xV5K6^ z9ak3#wsY>Y+ii`Fv-?-#6$miU$7mOzu*#VqWB^}UQ})hZsyvdzrXdrqK$lr!9d!jN zasovq=fS-)Qr9a+;|WH6BnhrJzpv~Z96rv75^u|E|%LkAEsIW~_pbn1*vr|EKwt6Sy$)U%8#Hf}AzVw;#Br~Iu%bp%F zCnuIsX**wUPtO%czW6N1Z9u~S`Y>vNd!Xo5)- zp(s|)iHO3n#z+#X#PN`jNHj6dD@BMp9h}HR9T@Rjc1>l~99MvbF=>fc$Ou$^e&099Gra;J- z@Qrr>IF}9od_h-_xfK>)z#qbu?jG{rw`OhN6Ywqd@Bi@AE2(~kkOx&xc?VW+rrcz$ zw7s-w$!afEIS5X$H*>(cEQXS7lsg#SZuZkk*n&qw30N#I7ou|4Z=UamAT&n7*V_S( z1#fsU)A#rl@J6yZ1Vns)E`P(w=9izeqqSj;z=@PjFa=o#Z<~NV!AT=dzCGR%IY&ed zrMPZtpP7{vBSIn@T{vA5Q0K~R+(zl^z&>Uemkuq?-dRPRSs3l29JX>(`|G^vw{H+} zH@cVm1>5K~Z`j$fkl;+izMd(1ym=w2eh4a{xlz1o8=-3oIdFsVhT_d{uN+y+l{B13 zAg{>ZyHjv*yaJm&U=_C5qYvHysowKy-%+N41Lls=Ke47^Pytd?=Rm^;E&^3fa2lcX zXF(_%00f)+#@NQ3Bj&C2Ynke{cK=Ai^vcTD0T+d3;e7tktre-?c1qwDU+lPB(UDp0 z?D5dIe~3$DP+%*EsZ|7&QQb!N2Yh7OaT9>H- zOCx7*x-p=ueYzZ-8q0y0WzoY7Jx>@CtQ@>h=k)e~Lm|FAW;?Jp}Wn-?HuCGKFbJqLyC~#PHD193xo5Paxd9B(@22I zQ$jWo)nK`GA=$a->HHAvx+NQH3|fo!7IoTylww4%P*ob%i7OeDHS0N*GMLC;EP z|IpEr3gy~c?yiQ^2Ae(F4@1b=e*lg-dH4Js;S&sI#K&j!UzFsgb3R?#hLV!7)qL1; z19><)GUIN^aWWFPRp8+_fi>RL?}IlYeS&9!;av&`W=&t=FwxMobaV*t@qbOcAK^re z>ifcUW@pE)j=4#+1f7l@B)q( z^QQ*&IuHZ$$A=rT1pq_ zM|=z~on3oeHy-pL+ISBCAsA{vM=rbZ!6Y_BK44Q)6)>a*Ag@yOf!X35#U7l26|f)k zI44px?C%W)x6YVezm_1fHnIkucTwgVaHPnrz@KV74cYDYma_~g_Z~lgAemtL0r;06 zoKhY<0Xv`>56O|uz5x^Zgf5+N?a1aMJyAgUPeO9CkHFKp)w?9xN@HemQAb0A8YX@~ zN|iz@SF%qCi$sZz3;pEokMP_|Kd&B?4aU@6F7xfv{o7sCjCh$IOCfg+ut*0(;8;8f zDjFz7@?sNUFAzyw#0iNq;y^}y`be@_;P)2#md`?0l6TLqU)FaVlzlxE8-EnML#*z`B@I`J@|PUm5gasV=QrYIZ7f6XWa8#+sgi85&~LGJkhqHb@3IN)r=j9Hk0-Y)RuZwJlx-<6 z@Zy>KI4{FH5dQ$TB8Xy@uj1-vXT5}5LG?JbasI7nlXUJ}-fQ82aZy-nG+ zFlz^+#5{IP5?c9;NO9e7_Ny zdz^My7ZtHu)^Bh$Ec#jl?O*|QFeerX`J@rDh2xuv#9m=@MhUNOat{-u}VLG4jZIM;WJMn z<#o;bFhS10fA9J0%|gmwX5EFaEW~uE@4%fMcEam|Yw7ngIZ==4OnD8&cto5ZMiept zAOudxOn*Hb_uQ^7I6r!)85%wc_6tZjug8%U9W;Kc6Ov*W;8aap|I4G&gDOL8<8Z-9 zXCoDMIWfXEbu4-~RZOg1{kJ093W03OaWRM^Z(jAd?aP@kQe?epjH~-9mfBM*;cC^b z$!heRL6r1)I3EQS6tHQI*kw= z!dQ|o?&MU19(dxB-}CtMRP{w#Y#la_uiy3?jOexQPxQqlowz1^JUxBiBClE6*EVOp z-a6i5fnDgAZ-5CsRW=$7+VP4&_0VX&oeYLMJ%O<{K4MW=T3t#QCD!^TzcGM~?5aAk z&l2SL*%AX!31SyEciG-6s+DmN0(0!T|E8uy9^busI_)&^9q7+aZG_v!a-{e4Kxt2+ z9Qg&+F`ZOjqx~a`ZaB11hEh#^vLdGy95;c{{_2#hFWxvSPz#;QZI1u?Rk8*xtQ%!q zPhFt2uuuaGbbNkMh%3!KDwD>Eox63kP1ZA!nK6y_OAEmqW~QA;Q7K`5B{rvkzidci z&%3oi!|zn((6b)|D$$5ts!*`UQ_g(^C-6|E&RqedKN@oj&?jwQ;X%{$5C69MLAUJ$ zdR@E1H~ig^BExgzHE(XgCh`M}LtWbbsvDtII{G{B05E1-<((gvjC`2iO^>wB;kWB; z)NRV&!-kZ>$z@3k33gQe6B`;(V48zHk_kE{T5 z(ooMN@RFU6jP*AMO_;f%5@rr?@zD*_x0z8LorVgfn(7DJ>e_8Dqb6mOLD)sbtSi=&ymJB|Ffv>7=SX*7{hx zgW_nOs26my&}OYNsvXb|j2yJJafJie+bk%tf%j)+5RZk{rWQM4I3?9L#0MNl%4qFX zI6-pa^hM~u@H2|!Mo*P6gh33?)q8f)Qmj!lrnte)gC;vSbJ&*WR?5@7<(T$^_$c)? z$y(eCbO8A$emBBzN zqpwI*hW2=fs8^U47c#@(VH!9;{!0c9YJXd5l z;|VRt7N&iv6eSY#RQv}Uh~1xViYS%y>A_PhhV>mSm$OVQWlK3RA*#6sM!qzLC1mF? z|L{DrLXm1oMk${J|gc_p_e0rfyFL`D)qW)j@V9qrS>R06?_% z|J$xmd-TA6a%&Dd+-J4Gu+6QOrpM76F2HW4=vNl4j4|pE{i+hqW(z=`Fe+jgQGg{! zrpI9aOwFUkB~n)>;6D3;7UjaLy#lg2fV03<$w~2N?d|P-|Ni|Xm%O(tqyPXJz)Mz7 z9QgWJDeUgy!C`f16clv2RrW@QK2IA6QKabi zh_!=VQsWW;TJYR&*Z&}t)G0iT%bQC+7QUehLwGMK#gAk8N-e!KzEqM}+v`ijecG9h zm@;35oTkyYVmCl1vGAJ+`C-Xs;z*qc9bs-Abkw6_8-#%zF+bbYcSKEP?<_z8*9^xuC3T zWk|H*6syryUa4d>0*}yA6t9{vI{-G~4T{GKcYHFqID*3Y>P+ek<+aJ1ulqy$pl?V6 zkjvPp^r)fo;g6-K+mfNG&5rJlvxs~KPUmWsn+m;?JWooaBnz>|6fR!a-*=|$a+Mm} z&LxY(8kXip#22*bC8H4mY*t*hwzf%r5Qm+Tb zCX@)iKc&pIrFehO?hL(v?}d}HH)Cdx8rFejLVe}0f8Qn5!(RC~@HP5|dieHY1>EXl z2O|`tGEm&w%=BOZ@`UEZHb@rITCdeeX?iUk^L+3LdgMbm6n^XzM!E0(V75wsD&;Ku zH>y1$r|X^0%ELJt>Pq<^dk1$i4^E#%I6zHy9ncB@qOs8Mp5~7mygqt(m7}0?f#s@{ zn9L|)`CI#ckwG(G(>^oqL+sa(vE!POR>PB$9!ZahHPJLs{ARg&Pff}G{CuRURAvj7 zhgGyHpaTJKrH#r`{~dG_VhA*@_K}m{IRkqJQeCJkewJ850=AR4(*!DY*)@;^1M_51#{}-Gt)o@rg7GsU?APmxw^4-SJirK4D|;_45q zg=vlU=&?p9M$$4D7Y}iKC$XWcJ(`kIOi+10Fo`6=o?6Lmq(=|8B$)}v#5%zyXa287 z{R@(-6BVp7#7z>jI-- zkWWx7BdL2pBqN=SL0Ak1tuaXb?tmIcI#@mmj1`Kxo`Cfp@4+;Z0w};nf(3>*eCa_C z!)Xv=i1d#_r-x^Vg!XjW8hL#(4PxK!Tvy2XaX;TJ~D8OQg7$}l@@gx z*L>miXACs7o>FyuwhGx3H20_+0SjjH6W7~Pc-9thsK^^mI}!i2iL0!h?s2X>RPKT2 zeDKw5g)*%%f`#jLTDP3Eoamyw$;iSQVsxad^)x3Jw%gNIAlZGx4i$jSIfV)Bo|Jyh zam@};ZqBm-%8m^ty5w*r9prvms+yIt!m7rjpbzGrSIt=BS>v6+ppoO#FQx)aCc0Un z&MJ?J>XWvK?YI^(oy&) zCrKGokJIaN_6{~~|3(>tXEVc5;`y~{ZICBX(H+%iUWjSnViu9As=cUBJy>4(PHu3m=x>2%E%pKv12y*d&>YG1W+Hv`O+fNT8b{M?# zLFz14BRIxplA6=68rQNOaLH+jJYP~_$^`W@C5x-b2+aMG%eGHj-xU=d>>y&iI#WPH zwL#Our{r2BWd|FBz0wT+R;JzMg3|FkOKi37q{hQl3PwBAE?URuIJfeh#pqGB@5{k`92~6sO5kDD zY|w7y+xG1<*`(9rl2EizKd?!?%jSURv!G`5ZnQJz1}auS+JI?G)ji7W*^i$U4~5`8 zl*P0}#V>%HZXtQlrAwcvy(6ya=A40qj9TYa7SVcA0epRMlqZWE~E(!ri056Tt3g+8iM0rfz5`GC4mtX6&H zH!T*@PrPOfm)g+f;Hi7hI)}JDbe#y{Yj)3%gJfnuNa!wrcQ2S4w{v-yk&7wE&6h`g zIwm<1sNP$_lU{zj61GlQRu$e^xj5$jJ$vqJ%D>?=xE&(7@^X3)IzmXR8%nw_@3%Hs zq;Q{8&u8zLVZ`XA2CK_{HZ$XRa{_az)D|yqhYbnd|9J~l5V9{h8|;{TDFw;EkaGtC z_#`H`ckV{C(ghBQ)h{yz%x9;1aeM8x#`q~KD-U)l98Kp=^CP!Yk5Q3|8n@}qmoAZ0 zjufiOZBZxEQIMVK-eR$`vFY-3>o1=m7UXBAC_ksR*FYkQ*~nWy11m8hfm)T6@OZ`_ z@(|q6weZ(sD-$9?h>bv?@%{%wuGJuHvdfFj%Epe06yfr z0aX>hfu737oNr@AWXdm~5o(Y8SHUMRBV#F2Ck^%dOiDkiT3Vn|1qiqduWWNDSP1UK zcj*CDMeZ^BaVM$%v&Qj0`H#QA5%vAIV_58C^1^3I6&#&raVWoUSMXA7R`nkmh~ni7 zCHnQW^+e>PvdATujnQgG(wBF!Bd-N6FGSaJ`JjbD?&5wH~=wA++JTog)8 zCoo*VAPl=d0PnFOxH~){3Y=xYb7SY;CDc{>ZnqmlBmH zm|JT-3}(;=2h<#vf6eN;Ropd_*1?Yfn*WEXI2v&Ib6kLiU5AE5)Pl+6q9a=QI5Q zG^K{)tw8;+!o^DG5Af)_i40=LrC~T^`tlZ4iQNCV9`*#Mj$|nv0d9f z@5MR4hcpIu6dAkIo5|=Vh1ODl1tK{Wm|)olqNbT=Q>-^xcQ-;8sKt~MQaJ&A5`P26 z-r1u50$xKa5FHXQ|S*Zxh`g>vBF;S)zS@80invIP>aCo!bfKLAa|p2V|%Mok^) z3^2(xP!T69*(L8EislYe9aKM0n{fe;-|+y0`E7qGrW8yU-a&XWOK^fcwCOQ@FAC)*#56?dF=C82QofShiB$V@~| zmKhLS6qOt%E~w5<4odp1be8LDx8|l7v6EEE6eu>=CAI{Lx$y$%a&MyzEmJsG&ez+o zG&z!dfeUl2lvnHzq>5NTqx~%_M{H}&W*|Duy9{pAU#bHN}G~!b+s40IB z@qPg`#L2w=9nzhvdTeP*?{d&;yzugz?|Skgt|NY-a_LI>!XIn^1tXg8tH5np=HLcn zaBVrkq^%yX#B)-(b8lsVwb?usegj&~vubH`feU13DcvBG7zJ8Rr|Br5`1PQf)g~@) zn0quS-s*J@ndf8T4cjSh!`H&Ja7cbM{LpSuE+BrAv?h z`U}CXB^a1e0MW?gyFlyFarrz^AB3#;i#OaVs9A3Sf6@W+2M|N9%arZG{U(_3iK7MX zmF0O>l8MSaVud4cb5u*q@`s#VJU*bB*a%%VNnuo}jNL>3`h874U2@+uQs)rFHSh8! zUbBz3wx-5YLfXuj>KR2(=^PDWBQ0@;A`CJ2EOMrQNJYC;hq^kMdJy|>p`pMY)n)UN zf@bGX9ku~(4%UHwVC2+u7bTj?(=su^jFW;R%!K`;fgyYf_d5odF{{&r0fZs`-?iK$ z%#`j>k4UQ1Z9T-`>tjuZ&c`WTW8+DgcEe$7jjHhJW|(G6uI!GvgM$M~KCn`7JND|c zqO9s|8&v*$Iy5`k1MaoUN}t*PzH+p+1(zeJe|lc3K=q}FbA#tq{o$DR9tiZV){o zvsdrq8)I2Rb(M$bK)KDQ!!-Cm@#8H)59j}mMgN2e1|7Hkvpe-VyEvvN5bD5rH{GB7 z3RoJx*xS|halx8^qjvsFKn&AhkNf`1moJ3xvkZ-w!#wjt{s5)y2Av+Da_aY&e(%@0 z!(ix!bD_l%KkGA!xeON;714c=*!!#|fSa?O>BbFNQTmN~1@7peIXbUkM-bE{aJrps zvF(n_3-HkJ+eHoPDFAj7S2}GM&%#8+?!>$2%zyqSf$}El-M(Azg0}fkDS=$X>}&R! zmLOTZ_$i5j5O!wY4^7kF+Dg!lg=URCpceyvs9k(k^W%{8 zXZ4Od{{zwj6Bxo3HA#^Vf;s!~399eyL}-Cn&r8NYQaE)zA4sugzT&snocMmyPx_}~ zB1H9fTUui>Z+>B6H*c>|NrZiF6CL*-gGuLAm*~R}K@`#Fu*M)<;nUqS5@euh2JjDi z$6x0w=A9Nf?^siMT@a{qZ}4s;$tfu!F< zX5U(~r+T4<=i0mQF{yxN&*dXh#G@o7q`EA-yCEsFSzn}Rj5_kq)0v#uoM73n8*UFn zOjJKC9)-2rwlRB>|ABQ|SDS9#=63iwTarfKDV_Avowv#<%uUPtQ2 zj^B`wlFIeDACxN?&XXHjx(IM;5zC=SsJ380t>r+ajMSokC*>J^`+f2_X>_~HfsE>o zIN*})?}Jo}*_(?@0`Pu~Lb1FuUfvjA91tm|XHYS>moiMnSOSG-a|9{bTi}C?cm$~T z0J8+$Q#N*Xm1*WAw^k6YRiBA!pAD@)T#j1vOu70o0ZeI(RUpV$UOq~1HJpKqlM_1l zjFzHz6J<{fITRSPfa2b!^xEaU*Q~71;t~xhmpKa9yI)>JTPJB>cZ?zTE)gTn$aZ!< zx^vk%j-kAAmvOWhIC>+W(%HGnzk$XijcR9wrQO+jfA@~{^Vnh>KQ)?nB&u~6ZR8^Y z8Fnv$yHb|^oBCHIMuHHkX=~uxes#bn&L0+U5Lg8c;p5|PpmH;wHHV)aIYwyibOXfw z`43y5PN8Dtmb~3}92~IUQ%FeJF2EF6NjefOM?*8NQ@V%URAXEFPZLL|=%i^4!h_=S zyzVsJy3YG~0g$)gyg#8BKg|rjy)_$!iS5=^^09_(|83%6SF@kZT^dOIwGeLyaZd^d zT~Md{0UvlXfG7zP6f$8f>cV8-84|>ydMTei^{SZ@`%HJy(%R=`YV5Vq#&|kyjyX- zo@0zx@uk;{_7N6zR%O@UQJqFde=Z?5t%BBC?=jQpzx(*fmyVABXFc{tL9>5+27_&Y z9;QHdm!2%3)yy8(ha6#usQwegV(q_S&-2S6Y#kS_OhQ=bR=`SKhayh*=vQ&9h(I%l zTcotMtQm9yM~j0Y=c|}YM2k{IuaU>UrfXn2ss(HirKXS_i45(TTNO56HB|^z%x8by zOPn4~Yda{$%2nT=s@%e6-Oaq7Z=jo&Bb1jBX{lok{Qgz5u(yDT^5=B|z!tO|<+9p5 z{{`FumZ6A6Crqb9pjSv3O>~YDm0UsgMM`3N^6H+xgb7q60@naX_sNbiV9rh10oL~M z6ScQ(0MaC1n$d0$UQ2?3GX5ic8)%Tp9G}^0)0}26-wlA9rg^iby2TefI=_f>L4`5~ zVd-H6a+M+D1cf@s3)53bp;K>rfx`K@O!RAs0f1OA+75{@_)zqcE=pF>w^C?%4XIAz zEsKY(2T(d(gM7bmDBipQ9%LoI@GqaHkH3|t8C`yk8y-|isTR6Fu9-`-7$PGT1!Vs} z)Cd2yf`f9PpaF5kJsbW_&^8nlctNW`hczgs+0$l5_4;M#Pu(jOj5}>-5C( zr0sbBj|eC@1NQZU^39thYdMjYJJNuq-rOGWbgOv!A zzHIG299K#66&fSqJHxK8NN;N%?Z-_acBl}{SD^nbKDSeZ7XEQWvSG0TyaUUV<)WbV zz#rH5k5ZV?U=t1Fk8?9iFed|71ecbb=CcwfI>Z=-ny~N+Sq^q1P;m*oNRflS@y-AG z;O{ZSpXecoBd(1Yq3Y*PT`(jivyaapw^4)}l~ycP$xoSPnEA$zA(TN14Pv z1zQ!rY32DFBzw}+r;ac)JuUm`18lBMm5!filifHN^4p?iP22D#p$6gcZ`Vsk5UpeQ zP!T<~w`(2DoF{>)Q>I`x^hjQH-Eq}nv-X!KdXJ_U$I6jJCwO9hZIj4Pe%pEY;2{1u_+)7vjmopmYvM`r9L0063WZ-#ryX(00t&VYy`Sp8p=( zUS3=6EDgvd6@i&NANY!RBjz^C>u=KFL{0vQh^8Ouz(?uIIarTm?fDw%x%x7J#2PE=E2F zzs%io!&|er_>^ROj~5)tCsikH`E&r2*cW`akm#)E>wpjEIG1dDB>3f#`hf>%-KVlT z1pcz|ik`-J1Nw5d|5*wzb-V)y9_9*29j_Ca6&FGWSAmDUCacCjfj+biW_&-1mWJQs zr_-)Qw)pow>lW$|aXCF-m78KOtJLMTm)_lYh`_~*z&-CS&4z7^74Qg6eE2;-cZSby zhPm1r^vj7AHJ6^904l7L_BpiVwxr`@0&5|(1KCnC9f9Sce78T$+c29RlG38ACZx6uCL7#AOl65#h*FWQy}j;cu1ySGBR<%|31xvfblWU^8 z0EB>Rl0{I0L^~B9HzF}=$DidXB`PW^+_2ov*qG8QHPH06vCJLYfXKcZZz=W?2tw4Z_Cvzw_&+pcH}}* zKlEwb3k4_l42?*rjgr+?x_8;AX7-U_*t^g{av$U*3ue5nS4%>PYaGNAUf{X9NIOfz*0s zpAaNZ2$OL73GzEG$PZ`v(dK=sq~Qc_eZj0-$Z&5CWa7&7$#*gwOz8X@_fGL@c4RzA z949lkvX^ve1_unW`f{$dm0)QsE?`oPPuUsO1-Fog{u_o@WT3sXGf1~qYDg&O1d|$+ zCeG~u?|L*&j3C$x~GGtdgMw&NrySJCE;) zRYc8(pDf8fqnrpV(Kr`K2t7}A!u?|G!aJ&V{XO_Z5!9|N`K;k!5H^j*&~~T?2~Uyy znd$Q7J~>$>{Q6e($0onB69`J7{EqpC0%`F#WJ8;6S@R4f)Uc$RcpaVt~2>`&1(k?05-JlYwywhP$Lq_JLbzggl^;2Ig zB1Y82KI#6|PoIU=eqkGvK7=rRT?FW0wn9G9RQC+=?<*ar8VEsGe*|kt zWkdfX^Vv!WF3=&3?MQOiWXB@+MaVJ~5*Ydd6=bm1>jY<2$%PYoWIzSnq84PJ)e2SQ zTbCe4DJDm8Un~~CPW|Wy0mT$CymCc^f$;uWU22qQqRq{4@8e7FVe_5_S48!1ZDRLo zSYB#g()%wg>-IVj9H4s2awn_rUM=l(x7t7D9PjMomTRW|hbqiHVo3$gR^$&+gNmop z3`(D6R=qWKTFL7frrkitP}(M(#BD<^xzxv&|5R^eq(9jhgx@|Yq@84mKVzE@T)evW z2`8ABPas||KSYyemziS_W~6g1H0cs%_9Qw-YY`-9p3Mc2k<>|810gyR;(n9_Ci>v@ zWm@*&;Gp!zI0v#%fR6?n4=#;Fr1yI=A_mJDk2V8-pvy%an#+13ebdVhYaFdMaba8n zudh+xeU2WC7eNm{G6y3(z~X(}!|?dH-)GO_f9d>6?1Z_f+0u3;86ya4B5G%d()aj0 z0ZOSfOkC6_8pX2;d}IcWV78HyFC~XULA3%721U{;D1U;!5KVu&UPx3uTKF{m{PIgg z<4cQD9@vSJSec1(7>fkmMK80=FNUnHAJ1Sil@19>rs11C?0j6~?}ndUQ9;>1z<7b$ zF*9{)h|cLIp-9w->)`Zc@>w@<@UvvRi0can;3;t8@nzj9-&6uZCm))Z6$pvf2PM5O zlo2u${EhXJSJi^(1owP6x2XG9x{aF%;qUy?^0xj$wN~yH#3eUCd=T0wuVJS7td2VX z_(qn$pI|3km*_#9yTp;my36x*h}R7NKeYj47E4e>MP%ab`8o|BmVK1dRR@R;S{i?) z^ULdB0j!-{9Pdn0JG?O2I&Ac5^>%qSRY6n*Tf>49y_d}Cwbq<4ZJnH^r@=2*I)fL( zQ!LLmA$#SXNY&ux2g2wozc7j51D2n*6hNh$bN*zm5P= zH`tKxu43eO-8JZ$^!*Fxc@DQ+HQ`CrxI?`!A?K*b&81(mTQ%Fk%JGXR-qHjJwHhE;w z5}=GyiXw`I<2yCJ#GdE8WxM{t=^}=C&6x(G2xMaD$2@y)Ck{rqtbcLB7qcJ8EOfW@ znd+^*U`i}{B9Qv_?Y+bEl4X0R0^KS_|D3l<2gEV&KL{HFSAURW8u?7FgEkT`kYzlD z#GVP+VeZcWv&WWHDlp;o`+!&m9C#NVy{sVhX?-C9B0I|8U9Ht128^}z#@yi2hj!*= zY{I(MbJLZQ^E8-9{sO$;Xg?G&Ws16^`OPmgyvPQ(->#%X1WdL_i?RAk-O~_+&5E)# zj|#iz1>n(6LJddt3XRkQ+^LXK{9dG6X(tGRP2KBOZ-XM$$WAE4(9m#uwkP@P=;&xp zoMT5C)_Jj!@AK!h{_-B`uXzz~XQ##u_ySJezp4+n(L|#y8&LP7>!j~3Gv56SkOtrr zL{ssw8U_5sW!twF%Y0X+*ENsZ;!Gs{)48@!dnDi8y;gvwgxb(CU;3;-N(R&o%kciDlm4MD_KYeTqp4z5z=yj@B?GhFF}0PqGjj={PwS*I8F>c zY3EFjoE9;7hx23I_eZR{?>Ta?ox^lhmsx%|y{kC8--YGK-@O?tJ61mpp1)=!jy*60 zukQxSf01TC9D1oghwk_*3(%u;J%)i@AmhMyFwWG?H}?f93w`3GWtc5lE4J82j0byB zmzUQO*oeaX6eS)5;)}M<9jNV;eE!krmxVR>K)))UkQ7-m>Z*c!4oE|vp1K(bK%$hU z+8)_n)->K!Z~~(u8_3FW+NbERNmotH1yV>d_ZNbTo8nFkuZ zPnNrTKUD0SeJIKQIG5R$AN@@QoSMK#XZNYf@8|05!hz<8ByHd0)9;GT(67F zwDmzcWuMAZ<{!?_ZO5jjP>~l8`eyywvScn+gT<77hsYrMZw{t!W%+U6?%spH>STeX z^}w5aQQIgg+;_l`&(Kc>S^6zfoEeP@8P`%#QMq7`>NB=~Uhv3`bV95ALY4|-GiCim zp|OUK*hrj!fj_$QF~X8EJ$h?G)VVx2R|aJt=|-uP*B7mt+w(5AcR}L=7(?Lx0(@lp zlO9KJ^n#te|Gk#3DJ?e7q7^Zb{*tJ6QIv>QM7#d4l7`DM=UshjBPz_?-fgfA%X$GJe5|FSV=wZ z5H@T7_LPnwP~1MoL>Z_6-s4$+eR)@j`FcF)Et;;!*rElI-;1LFKeBg4O^5^8c26Z<7M{`xBxaM z{ucRLw9`Y(H`ctE+=ifo>>CU(Uk3;F67!zdAap|q^FCU7vw+gNo3_jF%#rThhO{^E zbTCYlUG-;*{wc(DCT=s8xFEJd`ww07EOzW$k}FtxiOn5FO+>N!Hi~+^0pn_aO?eD0 zhQs7r@aqK5Q|!@wPo#pEmH&!bT4+M#QJfLw5F@2EPZT=`Jc4h+_?oBEY`h^MTK~{eT?Tg9ir% zXYGT3la-a#!NGx|e@D7SBB2j>HqHxqd_)e~cTI#TQjJj|rED~}A9%w?q*}H>0P%yg zA~5kX-$DU6kKq|U$P5*&jWX?DVolklz5oI<0iO-hrdCT}OrIqwG_u8#s+SAb|vSU&;2J2FE{JiM`vHQ8??Q|)j- zWm&2J6BWf~H8{6jOR=!9f3Lv%x}52+S7Sz|Rly%Tv*3{}#>H*3oIfhaC3HPv+2tNc zN<#karSr`G`+O1aZ8$7rWjTHmc6b}xWo`3?%LyF4A8DaH-*ostQ_`8_+Hf0&O(|E=}JUv~owP-jN! z5xflO->}2Okb+^$N33T=M#-6FQ(d;6nsP(6;P6$UAsoQue4Uq0KxC*qg4SE$sn$r@ zUsSs<#nq8+Iqf>mY|}~G^a~u^`z#$omyWCQq7nBE`zaGu z%&66mFc&vays{B&FL|uQ{ukQ7PEKVF9_$DGQg-?IM4=K$`gQf=q`d`ZU87{>jwcx| zk=-dsWDgy<7&rdUiPRK}$A2L2lFmRA(~-W!_inuZHy_EFnfcjwWv@~1Y~yq)@piy_ z8*lw9Bg{P{QT!KC3fPDOuB)c1g+x&{p+0xWl51Jbh4pdBJqE)d1r~U$x%p=LL@hBga)AIP*`F3Mr1s&_FlOTuL#$w+V2^!l!8u-ZyzPIOR~&ZxG6q0iK8ui8gB~!C&V8UQJmzHT#_ieJ?>r>$P5wU%I)CXsP%#$VnyDl>a48 z(XQa`FR$CT)TBzutgZG>C1QbR$&UkBTVgURWlch2XZX&bV?qpA_Kr2!2njPQmk9CO zn5Ag>&NISxCR0RNo3L^EP+Th4icZ>gQ)NU)F5FQ|KY^1B*A3u(Ya(eVDMRO#n6PC_ zC$iEl!U#nuP1aGoj6PU{X9EBK!XnCHVw$iMGi&AM=h=8(-(>O$S4plJtJ&_E^PCZH zi3y09F%v4EisE|dX9UXJ^*oRk5VdJxK;phT6Kr+ZBupz@#OgwYyH?dsv zAy{(Dw+th>S|AAt9v+W67o+s>B#2>Ekz^?B7mgL1LDtFq}3gaf`${)F3`XoVu3=gZcB0@BO%zM*!0ej8hh!xCs`^gtE$jpkE; zZDC)VYauT5{|T#O{u}*IXlalR2TTBO+{?d3D`-M3-Q2`NwF!Q&q6}BiCvk(viIV|5 zOZGi3YrCY+u)YhY^B5qfu(~~k4}jw__6z0$^$lS}2d)SO6L&xH4I3SP#m_@LHB#P_ zIqoM=4S)3qOvIRy?(blRcOm%|g*(h~*}j|`T!H+g8dop?jG)={Q)-~_`G^`fK)@51@l*xE8Q+w-t zU;qI)8T<{~NloW{^l8|5?Bo%+_^lr6uWD(qWsGagvY#;oAgZTZRO<5+J1!^uuqxu# zv8{QJ&GOr5QO2G8$>q1`Io~=pA*7^qugWzo35D^oa{E_k7A0RT@D;u;T=YU~mI2R` znTOOlVzLZl8CO))Eb!U}I8`_@+pd;oruSPwYKzAI+yUQ)%s>363sPg>Qw#8%u|IA5 zFVLc*wc%`~+ejhb66$1sV>urXB{(@bDec>%W*#E&!znukn@G&)U3Iz2LD>TEsia+r zG5V`U8Z6u~lytZXER%YLmUee*jZgt%bo;v=c6bzZ@}T>>G)?eK$hW52`is;Ne~cC_ z^X$nd1cUPvx4aTsuP5o23E!>bim=GVj4+`?_k1*aBJB4_RY!hhRPmR}tc;7|HtO^M zIly~1hALXAec}5hANC8qu=c(%fMM`=@m4oL1II?XJ!^+TLjPdvZYof8D;FV=kwd|s z5pGb?`9DFqez4G{lgdab-N|l0S*wm^A)6b`ErlEHb*QjNRn&5}T!9~10V zh&BihQ!z+(jX^F>Me*u1g)$Ri$}p{werp>RK?<4dC2KY8T?Q2EZ8{OavAx<1iS9+s znSE-(53Bh`*+=hFV5vcBC~C=tMlM;N2Y<2PX@Q^^Wddib&_qBsdXME z2Y{cZ0$sqXM9wejN=oQ%Xa&bkRIQKv=ygPzg+?8DU!?En+#p7bo zpA4WC7S(YEfpE%KW^zpZFlRX1<@lVm!<@cpobW`C`LK;-=Kf%g{Jm~)M zIaEXtZn?GHUnyn}FiqSz6$wG}dUZEk8J`KN%hmQxLGvs&EEaFKu-b=RO5JoHE zQc@zKmC5QSD>|-Nv*asRU-xjv+gPS~R$)FE^+Tl_Ra3z)e+nqUs@(*>rm3bP8M^C` zOZ`u(tUNLP_lvm`{^b?AnSfa>h7UE9gEbOh-Qh}IrswEMss7wmxLN)M`vqCJb(di*5bI?0Uce5L^Vft+@DX#U9v@fzN$izvSP%=;j zM-5VvaF+N{Bh^zCu>LBG{41h#gJA7f4vokeKEo(2!7$c_QE9$=4O{mRTN z%k_i8*QjwMK7w(4X2f5Ssr5{!L|w{^H0J0ba7TOU5W


o6Bo;LGdlC!j(+CBEhf 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%0AB#RvUj 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 literal 93459 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

j*?qyD3h(bl7c{~hXOxb8Aj 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 organizationId, string auditCode, Optional messageTemplate, TemplateArguments templateArguments) { - return new Created + return new Created(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, OrganizationId = organizationId.HasValue ? organizationId.Value.Text : null, diff --git a/src/Application.Persistence.Interfaces/EventStreamChangeEvent.cs b/src/Application.Persistence.Interfaces/EventStreamChangeEvent.cs index 150be808..77c260ae 100644 --- a/src/Application.Persistence.Interfaces/EventStreamChangeEvent.cs +++ b/src/Application.Persistence.Interfaces/EventStreamChangeEvent.cs @@ -10,7 +10,7 @@ public class EventStreamChangeEvent { public required string Data { get; set; } - public required string EntityType { get; set; } + public required string RootAggregateType { get; set; } public required string EventType { get; set; } diff --git a/src/Application.Services.Shared/IEndUsersService.cs b/src/Application.Services.Shared/IEndUsersService.cs index b6a8888b..a6e19e9b 100644 --- a/src/Application.Services.Shared/IEndUsersService.cs +++ b/src/Application.Services.Shared/IEndUsersService.cs @@ -6,9 +6,6 @@ namespace Application.Services.Shared; public interface IEndUsersService { - Task> CreateMembershipForCallerPrivateAsync(ICallerContext caller, string organizationId, - CancellationToken cancellationToken); - Task, Error>> FindPersonByEmailPrivateAsync(ICallerContext caller, string emailAddress, CancellationToken cancellationToken); diff --git a/src/Application.Services.Shared/IOrganizationsService.cs b/src/Application.Services.Shared/IOrganizationsService.cs index 101abac0..16fab2be 100644 --- a/src/Application.Services.Shared/IOrganizationsService.cs +++ b/src/Application.Services.Shared/IOrganizationsService.cs @@ -1,6 +1,5 @@ using Application.Interfaces; using Application.Interfaces.Services; -using Application.Resources.Shared; using Common; namespace Application.Services.Shared; @@ -10,9 +9,6 @@ public interface IOrganizationsService Task> ChangeSettingsPrivateAsync(ICallerContext caller, string id, TenantSettings settings, CancellationToken cancellationToken); - Task> CreateOrganizationPrivateAsync(ICallerContext caller, string creatorId, - string name, OrganizationOwnership ownership, CancellationToken cancellationToken); - Task> GetSettingsPrivateAsync(ICallerContext caller, string id, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Application.Services.Shared/IUserProfilesService.cs b/src/Application.Services.Shared/IUserProfilesService.cs index 62bc3705..3d128aad 100644 --- a/src/Application.Services.Shared/IUserProfilesService.cs +++ b/src/Application.Services.Shared/IUserProfilesService.cs @@ -6,14 +6,6 @@ namespace Application.Services.Shared; public interface IUserProfilesService { - Task> CreateMachineProfilePrivateAsync(ICallerContext caller, string machineId, - string name, - string? timezone, string? countryCode, CancellationToken cancellationToken); - - Task> CreatePersonProfilePrivateAsync(ICallerContext caller, string personId, - string emailAddress, - string firstName, string? lastName, string? timezone, string? countryCode, CancellationToken cancellationToken); - Task, Error>> FindPersonByEmailAddressPrivateAsync(ICallerContext caller, string emailAddress, CancellationToken cancellationToken); diff --git a/src/BookingsDomain.UnitTests/TripSpec.cs b/src/BookingsDomain.UnitTests/TripSpec.cs index 139ed65e..1dad60c2 100644 --- a/src/BookingsDomain.UnitTests/TripSpec.cs +++ b/src/BookingsDomain.UnitTests/TripSpec.cs @@ -1,7 +1,6 @@ using Common; using Domain.Common.Identity; using Domain.Common.ValueObjects; -using Domain.Events.Shared.Bookings; using FluentAssertions; using Moq; using UnitTesting.Common; @@ -21,7 +20,7 @@ public TripSpec() var idFactory = new FixedIdentifierFactory("anid"); _trip = Trip.Create(recorder.Object, idFactory, _ => Result.Ok).Value; - _trip.RaiseChangeEvent(TripAdded.Create("arootid".ToId(), "anorganizationid".ToId())); + _trip.RaiseChangeEvent(Events.TripAdded("arootid".ToId(), "anorganizationid".ToId())); } [Fact] diff --git a/src/BookingsDomain/BookingRoot.cs b/src/BookingsDomain/BookingRoot.cs index 15f3a4ec..d455ec8d 100644 --- a/src/BookingsDomain/BookingRoot.cs +++ b/src/BookingsDomain/BookingRoot.cs @@ -195,7 +195,7 @@ public Result StartTrip(Location from) return Error.RuleViolation(Resources.BookingRoot_ReservationRequiresCar); } - var added = RaiseChangeEvent(TripAdded.Create(Id, OrganizationId)); + var added = RaiseChangeEvent(BookingsDomain.Events.TripAdded(Id, OrganizationId)); if (!added.IsSuccessful) { return added.Error; diff --git a/src/BookingsDomain/Events.cs b/src/BookingsDomain/Events.cs index f0af4d7c..79d07207 100644 --- a/src/BookingsDomain/Events.cs +++ b/src/BookingsDomain/Events.cs @@ -7,66 +7,65 @@ public static class Events { public static CarChanged CarChanged(Identifier id, Identifier organizationId, Identifier carId) { - return new CarChanged + return new CarChanged(id) { - RootId = id, OrganizationId = organizationId, - CarId = carId, - OccurredUtc = DateTime.UtcNow + CarId = carId }; } public static Created Created(Identifier id, Identifier organizationId) { - return new Created + return new Created(id) { - RootId = id, - OrganizationId = organizationId, - OccurredUtc = DateTime.UtcNow + OrganizationId = organizationId }; } public static ReservationMade ReservationMade(Identifier id, Identifier organizationId, Identifier borrowerId, DateTime start, DateTime end) { - return new ReservationMade + return new ReservationMade(id) { - RootId = id, OrganizationId = organizationId, BorrowerId = borrowerId, Start = start, - End = end, - OccurredUtc = DateTime.UtcNow + End = end + }; + } + + public static TripAdded TripAdded(Identifier id, Identifier organizationId) + { + return new TripAdded(id) + { + OrganizationId = organizationId, + TripId = null }; } public static TripBegan TripBegan(Identifier id, Identifier organizationId, Identifier tripId, DateTime beganAt, Location from) { - return new TripBegan + return new TripBegan(id) { - RootId = id, OrganizationId = organizationId, TripId = tripId, BeganAt = beganAt, - BeganFrom = from, - OccurredUtc = DateTime.UtcNow + BeganFrom = from }; } public static TripEnded TripEnded(Identifier id, Identifier organizationId, Identifier tripId, DateTime beganAt, Location from, DateTime endedAt, Location to) { - return new TripEnded + return new TripEnded(id) { - RootId = id, OrganizationId = organizationId, TripId = tripId, BeganAt = beganAt, BeganFrom = from, EndedAt = endedAt, - EndedTo = to, - OccurredUtc = DateTime.UtcNow + EndedTo = to }; } } \ No newline at end of file diff --git a/src/CarsApplication.UnitTests/CarsApplicationSpec.cs b/src/CarsApplication.UnitTests/CarsApplicationSpec.cs index 26df4673..3ceeca48 100644 --- a/src/CarsApplication.UnitTests/CarsApplicationSpec.cs +++ b/src/CarsApplication.UnitTests/CarsApplicationSpec.cs @@ -329,7 +329,7 @@ public async Task WhenSearchAllAvailableCarsAsync_ThenReturnsAllAvailableCars() ManufactureModel = "amodel", ManufactureYear = 2023, OrganizationId = "anorganizationid", - Status = "astatus", + Status = CarStatus.Registered, VehicleOwnerId = "anownerid" } })); @@ -348,7 +348,7 @@ await _application.SearchAllAvailableCarsAsync(_caller.Object, "anorganizationid result.Value.Results[0].Owner!.Id.Should().Be("anownerid"); result.Value.Results[0].Plate!.Jurisdiction.Should().Be("ajurisdiction"); result.Value.Results[0].Plate!.Number.Should().Be("aplate"); - result.Value.Results[0].Status.Should().Be("astatus"); + result.Value.Results[0].Status.Should().Be(CarStatus.Registered.ToString()); } [Fact] @@ -369,7 +369,7 @@ public async Task WhenSearchAllCarsAsync_ThenReturnsAllCars() ManufactureModel = "amodel", ManufactureYear = 2023, OrganizationId = "anorganizationid", - Status = "astatus", + Status = CarStatus.Registered, VehicleOwnerId = "anownerid" } })); @@ -388,7 +388,7 @@ public async Task WhenSearchAllCarsAsync_ThenReturnsAllCars() result.Value.Results[0].Owner!.Id.Should().Be("anownerid"); result.Value.Results[0].Plate!.Jurisdiction.Should().Be("ajurisdiction"); result.Value.Results[0].Plate!.Number.Should().Be("aplate"); - result.Value.Results[0].Status.Should().Be("astatus"); + result.Value.Results[0].Status.Should().Be(CarStatus.Registered.ToString()); } [Fact] diff --git a/src/CarsApplication/CarsApplication.cs b/src/CarsApplication/CarsApplication.cs index 83014023..66b4c178 100644 --- a/src/CarsApplication/CarsApplication.cs +++ b/src/CarsApplication/CarsApplication.cs @@ -344,7 +344,7 @@ public static Car ToCar(this Persistence.ReadModels.Car car) }, Plate = new CarLicensePlate { Jurisdiction = car.LicenseJurisdiction, Number = car.LicenseNumber }, - Status = car.Status + Status = car.Status.ToString() }; } diff --git a/src/CarsApplication/Persistence/ReadModels/Car.cs b/src/CarsApplication/Persistence/ReadModels/Car.cs index e0f667f3..e822bdbb 100644 --- a/src/CarsApplication/Persistence/ReadModels/Car.cs +++ b/src/CarsApplication/Persistence/ReadModels/Car.cs @@ -1,6 +1,7 @@ using Application.Persistence.Common; using CarsDomain; using Common; +using Domain.Shared.Cars; using QueryAny; namespace CarsApplication.Persistence.ReadModels; @@ -22,7 +23,7 @@ public class Car : ReadModelEntity public Optional OrganizationId { get; set; } - public Optional Status { get; set; } + public Optional Status { get; set; } public Optional VehicleOwnerId { get; set; } } \ No newline at end of file diff --git a/src/CarsDomain/CarRoot.cs b/src/CarsDomain/CarRoot.cs index 3ddb56cb..f6b1d8bd 100644 --- a/src/CarsDomain/CarRoot.cs +++ b/src/CarsDomain/CarRoot.cs @@ -92,7 +92,7 @@ protected override Result OnStateChanged(IDomainEvent @event, bool isReco case Created created: { OrganizationId = created.OrganizationId.ToId(); - Status = created.Status.ToEnum(); + Status = created.Status; return Result.Ok; } @@ -143,7 +143,7 @@ protected override Result OnStateChanged(IDomainEvent @event, bool isReco } License = plate.Value; - Status = changed.Status.ToEnum(); + Status = changed.Status; Recorder.TraceDebug(null, "Car {Id} registration changed to {Jurisdiction}, {Number}", Id, changed.Jurisdiction, changed.Number); return Result.Ok; diff --git a/src/CarsDomain/Events.cs b/src/CarsDomain/Events.cs index 50fbb653..b39edd37 100644 --- a/src/CarsDomain/Events.cs +++ b/src/CarsDomain/Events.cs @@ -1,5 +1,6 @@ using Domain.Common.ValueObjects; using Domain.Events.Shared.Cars; +using Domain.Shared.Cars; namespace CarsDomain; @@ -7,51 +8,43 @@ public static class Events { public static Created Created(Identifier id, Identifier organizationId) { - return new Created + return new Created(id) { - RootId = id, OrganizationId = organizationId, - OccurredUtc = DateTime.UtcNow, - Status = CarStatus.Unregistered.ToString() + Status = CarStatus.Unregistered }; } public static ManufacturerChanged ManufacturerChanged(Identifier id, Identifier organizationId, Manufacturer manufacturer) { - return new ManufacturerChanged + return new ManufacturerChanged(id) { - RootId = id, OrganizationId = organizationId, Year = manufacturer.Year, Make = manufacturer.Make, - Model = manufacturer.Model, - OccurredUtc = DateTime.UtcNow + Model = manufacturer.Model }; } public static OwnershipChanged OwnershipChanged(Identifier id, Identifier organizationId, VehicleOwner owner) { - return new OwnershipChanged + return new OwnershipChanged(id) { - RootId = id, OrganizationId = organizationId, Owner = owner.OwnerId, - Managers = new List { owner.OwnerId }, - OccurredUtc = DateTime.UtcNow + Managers = [owner.OwnerId] }; } public static RegistrationChanged RegistrationChanged(Identifier id, Identifier organizationId, LicensePlate plate) { - return new RegistrationChanged + return new RegistrationChanged(id) { - RootId = id, OrganizationId = organizationId, Jurisdiction = plate.Jurisdiction, Number = plate.Number, - Status = CarStatus.Registered.ToString(), - OccurredUtc = DateTime.UtcNow + Status = CarStatus.Registered }; } @@ -59,28 +52,24 @@ public static UnavailabilitySlotAdded UnavailabilitySlotAdded(Identifier id, Ide TimeSlot slot, CausedBy causedBy) { - return new UnavailabilitySlotAdded + return new UnavailabilitySlotAdded(id) { - RootId = id, OrganizationId = organizationId, From = slot.From, To = slot.To, CausedByReason = causedBy.Reason, CausedByReference = causedBy.Reference, - UnavailabilityId = null, - OccurredUtc = DateTime.UtcNow + UnavailabilityId = null }; } public static UnavailabilitySlotRemoved UnavailabilitySlotRemoved(Identifier id, Identifier organizationId, Identifier unavailabilityId) { - return new UnavailabilitySlotRemoved + return new UnavailabilitySlotRemoved(id) { - RootId = id, OrganizationId = organizationId, - UnavailabilityId = unavailabilityId, - OccurredUtc = DateTime.UtcNow + UnavailabilityId = unavailabilityId }; } } \ No newline at end of file diff --git a/src/CarsInfrastructure/Persistence/CarRepository.cs b/src/CarsInfrastructure/Persistence/CarRepository.cs index d3a9afd3..63a8c952 100644 --- a/src/CarsInfrastructure/Persistence/CarRepository.cs +++ b/src/CarsInfrastructure/Persistence/CarRepository.cs @@ -7,6 +7,7 @@ using Common; using Domain.Common.ValueObjects; using Domain.Interfaces; +using Domain.Shared.Cars; using Infrastructure.Persistence.Common; using Infrastructure.Persistence.Interfaces; using QueryAny; @@ -89,7 +90,7 @@ public async Task, Error>> SearchAllAvailableCarsAsync var queriedCars = await _carQueries.QueryAsync(Query.From() .Where(u => u.OrganizationId, ConditionOperator.EqualTo, organizationId) - .AndWhere(c => c.Status, ConditionOperator.EqualTo, CarStatus.Registered.ToString()) + .AndWhere(c => c.Status, ConditionOperator.EqualTo, CarStatus.Registered) .WithSearchOptions(searchOptions), cancellationToken: cancellationToken); if (!queriedCars.IsSuccessful) { diff --git a/src/Domain.Common.UnitTests/ChangeEventTypeMigratorSpec.cs b/src/Domain.Common.UnitTests/ChangeEventTypeMigratorSpec.cs index 83ccb12e..780d9729 100644 --- a/src/Domain.Common.UnitTests/ChangeEventTypeMigratorSpec.cs +++ b/src/Domain.Common.UnitTests/ChangeEventTypeMigratorSpec.cs @@ -1,7 +1,6 @@ using Common; using Common.Extensions; using Domain.Common.Extensions; -using Domain.Interfaces.Entities; using FluentAssertions; using UnitTesting.Common; using Xunit; @@ -23,7 +22,7 @@ public ChangeEventTypeMigratorSpec() [Fact] public void WhenRehydrateAndTypeKnown_ThenReturnsNewInstance() { - var eventJson = new TestChangeEvent { RootId = "anentityid" }.ToEventJson(); + var eventJson = new TestChangeEvent().ToEventJson(); var result = _migrator.Rehydrate("aneventid", eventJson, typeof(TestChangeEvent).AssemblyQualifiedName!).Value; result.Should().BeOfType(); @@ -33,7 +32,7 @@ public void WhenRehydrateAndTypeKnown_ThenReturnsNewInstance() [Fact] public void WhenRehydrateAndUnknownType_ThenReturnsError() { - var eventJson = new TestChangeEvent { RootId = "anentityid" }.ToEventJson(); + var eventJson = new TestChangeEvent().ToEventJson(); var result = _migrator.Rehydrate("aneventid", eventJson, "anunknowntype"); @@ -45,7 +44,7 @@ public void WhenRehydrateAndUnknownType_ThenReturnsError() public void WhenRehydrateAndUnknownTypeAndMappingStillNotExist_ThenReturnsError() { _mappings.Add("anunknowntype", "anotherunknowntype"); - var eventJson = new TestChangeEvent { RootId = "anentityid" }.ToEventJson(); + var eventJson = new TestChangeEvent().ToEventJson(); var result = _migrator.Rehydrate("aneventid", eventJson, "anunknowntype"); @@ -57,7 +56,7 @@ public void WhenRehydrateAndUnknownTypeAndMappingStillNotExist_ThenReturnsError( public void WhenRehydrateAndUnknownTypeAndMappingExists_ThenReturnsNewInstance() { _mappings.Add("anunknowntype", typeof(TestRenamedChangeEvent).AssemblyQualifiedName!); - var eventJson = new TestChangeEvent { RootId = "anentityid" }.ToEventJson(); + var eventJson = new TestChangeEvent().ToEventJson(); var result = _migrator.Rehydrate("aneventid", eventJson, "anunknowntype").Value; result.Should().BeOfType(); @@ -65,16 +64,16 @@ public void WhenRehydrateAndUnknownTypeAndMappingExists_ThenReturnsNewInstance() } } -public class TestChangeEvent : IDomainEvent +public class TestChangeEvent : DomainEvent { - public string RootId { get; set; } = "anid"; - - public DateTime OccurredUtc { get; set; } = DateTime.UtcNow; + public TestChangeEvent() : base("anentityid") + { + } } -public class TestRenamedChangeEvent : IDomainEvent +public class TestRenamedChangeEvent : DomainEvent { - public string RootId { get; set; } = "anid"; - - public DateTime OccurredUtc { get; set; } = DateTime.UtcNow; + public TestRenamedChangeEvent() : base("anid") + { + } } \ No newline at end of file diff --git a/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj b/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj index aa40f38e..571e51d1 100644 --- a/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj +++ b/src/Domain.Common.UnitTests/Domain.Common.UnitTests.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Domain.Common.UnitTests/Entities/AggregateRootBaseSpec.cs b/src/Domain.Common.UnitTests/Entities/AggregateRootBaseSpec.cs index dd3128ab..1b543c02 100644 --- a/src/Domain.Common.UnitTests/Entities/AggregateRootBaseSpec.cs +++ b/src/Domain.Common.UnitTests/Entities/AggregateRootBaseSpec.cs @@ -161,8 +161,8 @@ public void WhenChangeProperty_ThenRaisesEventAndModified() _aggregate.Events.Count.Should().Be(2); _aggregate.Events[0].Should().BeOfType(); - _aggregate.Events.Last().Should().BeEquivalentTo(new TestAggregateRoot.ChangeEvent - { APropertyName = "achangedvalue" }); + _aggregate.Events.Last().As().APropertyName.Should().Be("achangedvalue"); + _aggregate.Events.Last().OccurredUtc.Should().BeNear(DateTime.UtcNow); _aggregate.LastModifiedAtUtc.Should().BeNear(DateTime.UtcNow); } diff --git a/src/Domain.Common.UnitTests/Entities/TestAggregateRoot.cs b/src/Domain.Common.UnitTests/Entities/TestAggregateRoot.cs index 53f867a2..f0624572 100644 --- a/src/Domain.Common.UnitTests/Entities/TestAggregateRoot.cs +++ b/src/Domain.Common.UnitTests/Entities/TestAggregateRoot.cs @@ -43,20 +43,20 @@ public void ChangeProperty(string value) RaiseChangeEvent(new ChangeEvent { APropertyName = value }); } - public class CreateEvent : IDomainEvent + public class CreateEvent : DomainEvent { - public string RootId { get; set; } = "anid"; - - public DateTime OccurredUtc { get; set; } = DateTime.UtcNow; + public CreateEvent() : base("anid") + { + } } - public class ChangeEvent : IDomainEvent + public class ChangeEvent : DomainEvent { - public required string APropertyName { get; set; } + public ChangeEvent() : base("anid") + { + } - public string RootId { get; set; } = "anid"; - - public DateTime OccurredUtc { get; set; } + public required string APropertyName { get; set; } } } diff --git a/src/Domain.Common/DomainEvent.cs b/src/Domain.Common/DomainEvent.cs new file mode 100644 index 00000000..272a5d34 --- /dev/null +++ b/src/Domain.Common/DomainEvent.cs @@ -0,0 +1,31 @@ +using Domain.Interfaces.Entities; + +namespace Domain.Common; + +///

+/// Defines a base class for domain events +/// +#pragma warning disable SAASDDD043 +#pragma warning disable SAASDDD041 +#pragma warning disable SAASDDD042 +public abstract class DomainEvent : IDomainEvent +#pragma warning restore SAASDDD042 +#pragma warning restore SAASDDD041 +#pragma warning restore SAASDDD043 +{ + protected DomainEvent() + { + RootId = null!; + OccurredUtc = DateTime.UtcNow; + } + + protected DomainEvent(string rootId) + { + RootId = rootId; + OccurredUtc = DateTime.UtcNow; + } + + public string RootId { get; set; } + + public DateTime OccurredUtc { get; set; } +} \ No newline at end of file diff --git a/src/Domain.Common/Entities/AggregateRootBase.cs b/src/Domain.Common/Entities/AggregateRootBase.cs index 1f9aceb4..f9f5ec84 100644 --- a/src/Domain.Common/Entities/AggregateRootBase.cs +++ b/src/Domain.Common/Entities/AggregateRootBase.cs @@ -314,12 +314,12 @@ protected Result RaiseCreateEvent(IDomainEvent @event) /// /// Raises the to an new instance of the /// - protected Result RaiseEventToChildEntity(bool isReconstituting, - TChangeEvent @event, + protected Result RaiseEventToChildEntity(bool isReconstituting, + TDomainEvent @event, Func> childEntityFactory, - Expression> eventChildId) + Expression> eventChildId) where TEntity : IEventingEntity - where TChangeEvent : IDomainEvent + where TDomainEvent : IDomainEvent { var identifierFactory = isReconstituting ? GetChildId().ToIdentifierFactory() @@ -351,9 +351,9 @@ void SetChildId(ISingleValueObject entityId) /// Raises the to an new instance of the /// // ReSharper disable once MemberCanBeMadeStatic.Global - protected Result RaiseEventToChildEntity(TChangeEvent @event, TEntity childEntity) + protected Result RaiseEventToChildEntity(TDomainEvent @event, TEntity childEntity) where TEntity : IEventingEntity - where TChangeEvent : IDomainEvent + where TDomainEvent : IDomainEvent { return childEntity.HandleStateChanged(@event); } diff --git a/src/Domain.Common/Events/Global.cs b/src/Domain.Common/Events/Global.cs index 681c8435..79c88295 100644 --- a/src/Domain.Common/Events/Global.cs +++ b/src/Domain.Common/Events/Global.cs @@ -1,5 +1,6 @@ using Domain.Common.ValueObjects; using Domain.Interfaces.Entities; +using JetBrains.Annotations; namespace Domain.Common.Events; @@ -8,24 +9,27 @@ public static class Global /// /// Defines an event raised when a stream is deleted /// - public class StreamDeleted : ITombstoneEvent + public class StreamDeleted : DomainEvent, ITombstoneEvent { public static StreamDeleted Create(Identifier id, Identifier deletedById) { - return new StreamDeleted + return new StreamDeleted(id) { - RootId = id, DeletedById = deletedById, - IsTombstone = true, - OccurredUtc = DateTime.UtcNow + IsTombstone = true }; } - public required string DeletedById { get; set; } + public StreamDeleted(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public StreamDeleted() + { + } - public required DateTime OccurredUtc { get; set; } + public required string DeletedById { get; set; } public required bool IsTombstone { get; set; } } diff --git a/src/Domain.Events.Shared/Ancillary/Audits/Created.cs b/src/Domain.Events.Shared/Ancillary/Audits/Created.cs index cd25f570..3067e394 100644 --- a/src/Domain.Events.Shared/Ancillary/Audits/Created.cs +++ b/src/Domain.Events.Shared/Ancillary/Audits/Created.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Ancillary.Audits; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { + public Created(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public Created() + { + } + public required string AgainstId { get; set; } public required string AuditCode { get; set; } @@ -13,8 +24,4 @@ public sealed class Created : IDomainEvent public string? OrganizationId { get; set; } public required List TemplateArguments { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Ancillary/EmailDelivery/Created.cs b/src/Domain.Events.Shared/Ancillary/EmailDelivery/Created.cs index 142d5392..764d240c 100644 --- a/src/Domain.Events.Shared/Ancillary/EmailDelivery/Created.cs +++ b/src/Domain.Events.Shared/Ancillary/EmailDelivery/Created.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Ancillary.EmailDelivery; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string MessageId { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public Created() + { + } - public required DateTime OccurredUtc { get; set; } + public required string MessageId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryAttempted.cs b/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryAttempted.cs index 05dc3fbb..a283e09f 100644 --- a/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryAttempted.cs +++ b/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryAttempted.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Ancillary.EmailDelivery; -public sealed class DeliveryAttempted : IDomainEvent +public sealed class DeliveryAttempted : DomainEvent { - public required DateTime When { get; set; } + public DeliveryAttempted(Identifier id) : base(id) + { + } - public required DateTime OccurredUtc { get; set; } + [UsedImplicitly] + public DeliveryAttempted() + { + } - public required string RootId { get; set; } + public required DateTime When { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryFailed.cs b/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryFailed.cs index 5339e65a..f2eef431 100644 --- a/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryFailed.cs +++ b/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliveryFailed.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Ancillary.EmailDelivery; -public sealed class DeliveryFailed : IDomainEvent +public sealed class DeliveryFailed : DomainEvent { - public required DateTime When { get; set; } + public DeliveryFailed(Identifier id) : base(id) + { + } - public required DateTime OccurredUtc { get; set; } + [UsedImplicitly] + public DeliveryFailed() + { + } - public required string RootId { get; set; } + public required DateTime When { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliverySucceeded.cs b/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliverySucceeded.cs index f13df0b6..6c39974d 100644 --- a/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliverySucceeded.cs +++ b/src/Domain.Events.Shared/Ancillary/EmailDelivery/DeliverySucceeded.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Ancillary.EmailDelivery; -public sealed class DeliverySucceeded : IDomainEvent +public sealed class DeliverySucceeded : DomainEvent { - public required DateTime When { get; set; } + public DeliverySucceeded(Identifier id) : base(id) + { + } - public required DateTime OccurredUtc { get; set; } + [UsedImplicitly] + public DeliverySucceeded() + { + } - public required string RootId { get; set; } + public required DateTime When { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Ancillary/EmailDelivery/EmailDetailsChanged.cs b/src/Domain.Events.Shared/Ancillary/EmailDelivery/EmailDetailsChanged.cs index 194d69ae..9378b798 100644 --- a/src/Domain.Events.Shared/Ancillary/EmailDelivery/EmailDetailsChanged.cs +++ b/src/Domain.Events.Shared/Ancillary/EmailDelivery/EmailDetailsChanged.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Ancillary.EmailDelivery; -public sealed class EmailDetailsChanged : IDomainEvent +public sealed class EmailDetailsChanged : DomainEvent { + public EmailDetailsChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public EmailDetailsChanged() + { + } + public required string Body { get; set; } public required string Subject { get; set; } @@ -11,8 +22,4 @@ public sealed class EmailDetailsChanged : IDomainEvent public required string ToDisplayName { get; set; } public required string ToEmailAddress { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Bookings/CarChanged.cs b/src/Domain.Events.Shared/Bookings/CarChanged.cs index d073ad6c..55ea4082 100644 --- a/src/Domain.Events.Shared/Bookings/CarChanged.cs +++ b/src/Domain.Events.Shared/Bookings/CarChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Bookings; -public sealed class CarChanged : IDomainEvent +public sealed class CarChanged : DomainEvent { - public required string CarId { get; set; } + public CarChanged(Identifier id) : base(id) + { + } - public required string OrganizationId { get; set; } + [UsedImplicitly] + public CarChanged() + { + } - public required string RootId { get; set; } + public required string CarId { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string OrganizationId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Bookings/Created.cs b/src/Domain.Events.Shared/Bookings/Created.cs index dca639f9..6d0f4ebc 100644 --- a/src/Domain.Events.Shared/Bookings/Created.cs +++ b/src/Domain.Events.Shared/Bookings/Created.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Bookings; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string OrganizationId { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public Created() + { + } - public required DateTime OccurredUtc { get; set; } + public required string OrganizationId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Bookings/ReservationMade.cs b/src/Domain.Events.Shared/Bookings/ReservationMade.cs index 3e257ba7..10fc8f94 100644 --- a/src/Domain.Events.Shared/Bookings/ReservationMade.cs +++ b/src/Domain.Events.Shared/Bookings/ReservationMade.cs @@ -1,11 +1,22 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Bookings; #pragma warning disable SAASDDD043 -public sealed class ReservationMade : IDomainEvent +public sealed class ReservationMade : DomainEvent #pragma warning restore SAASDDD043 { + public ReservationMade(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public ReservationMade() + { + } + public required string BorrowerId { get; set; } public required DateTime End { get; set; } @@ -13,8 +24,4 @@ public sealed class ReservationMade : IDomainEvent public required string OrganizationId { get; set; } public required DateTime Start { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Bookings/TripAdded.cs b/src/Domain.Events.Shared/Bookings/TripAdded.cs index 79cca4c3..3b15d63d 100644 --- a/src/Domain.Events.Shared/Bookings/TripAdded.cs +++ b/src/Domain.Events.Shared/Bookings/TripAdded.cs @@ -1,26 +1,21 @@ +using Domain.Common; using Domain.Common.ValueObjects; -using Domain.Interfaces.Entities; +using JetBrains.Annotations; namespace Domain.Events.Shared.Bookings; -public sealed class TripAdded : IDomainEvent +public sealed class TripAdded : DomainEvent { - public static TripAdded Create(Identifier id, Identifier organizationId) + public TripAdded(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public TripAdded() { - return new TripAdded - { - RootId = id, - OrganizationId = organizationId, - TripId = null, - OccurredUtc = DateTime.UtcNow - }; } public required string OrganizationId { get; set; } public string? TripId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Bookings/TripBegan.cs b/src/Domain.Events.Shared/Bookings/TripBegan.cs index 7f3fea0e..07b7bd16 100644 --- a/src/Domain.Events.Shared/Bookings/TripBegan.cs +++ b/src/Domain.Events.Shared/Bookings/TripBegan.cs @@ -1,11 +1,22 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Bookings; #pragma warning disable SAASDDD043 -public sealed class TripBegan : IDomainEvent +public sealed class TripBegan : DomainEvent #pragma warning restore SAASDDD043 { + public TripBegan(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public TripBegan() + { + } + public required DateTime BeganAt { get; set; } public required string BeganFrom { get; set; } @@ -13,8 +24,4 @@ public sealed class TripBegan : IDomainEvent public required string OrganizationId { get; set; } public required string TripId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Bookings/TripEnded.cs b/src/Domain.Events.Shared/Bookings/TripEnded.cs index 2215c618..a0812c31 100644 --- a/src/Domain.Events.Shared/Bookings/TripEnded.cs +++ b/src/Domain.Events.Shared/Bookings/TripEnded.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Bookings; -public sealed class TripEnded : IDomainEvent +public sealed class TripEnded : DomainEvent { + public TripEnded(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public TripEnded() + { + } + public required DateTime BeganAt { get; set; } public required string BeganFrom { get; set; } @@ -15,8 +26,4 @@ public sealed class TripEnded : IDomainEvent public required string OrganizationId { get; set; } public required string TripId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Cars/Created.cs b/src/Domain.Events.Shared/Cars/Created.cs index dacbc524..50b8372a 100644 --- a/src/Domain.Events.Shared/Cars/Created.cs +++ b/src/Domain.Events.Shared/Cars/Created.cs @@ -1,14 +1,22 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using Domain.Shared.Cars; +using JetBrains.Annotations; namespace Domain.Events.Shared.Cars; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string OrganizationId { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string Status { get; set; } + [UsedImplicitly] + public Created() + { + } - public required string RootId { get; set; } + public required string OrganizationId { get; set; } - public required DateTime OccurredUtc { get; set; } + public required CarStatus Status { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Cars/ManufacturerChanged.cs b/src/Domain.Events.Shared/Cars/ManufacturerChanged.cs index 62de6340..c0761e38 100644 --- a/src/Domain.Events.Shared/Cars/ManufacturerChanged.cs +++ b/src/Domain.Events.Shared/Cars/ManufacturerChanged.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Cars; -public sealed class ManufacturerChanged : IDomainEvent +public sealed class ManufacturerChanged : DomainEvent { + public ManufacturerChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public ManufacturerChanged() + { + } + public required string Make { get; set; } public required string Model { get; set; } @@ -11,8 +22,4 @@ public sealed class ManufacturerChanged : IDomainEvent public required string OrganizationId { get; set; } public required int Year { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Cars/OwnershipChanged.cs b/src/Domain.Events.Shared/Cars/OwnershipChanged.cs index ef2211ac..394843b6 100644 --- a/src/Domain.Events.Shared/Cars/OwnershipChanged.cs +++ b/src/Domain.Events.Shared/Cars/OwnershipChanged.cs @@ -1,16 +1,23 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Cars; -public sealed class OwnershipChanged : IDomainEvent +public sealed class OwnershipChanged : DomainEvent { + public OwnershipChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public OwnershipChanged() + { + } + public required List Managers { get; set; } public required string OrganizationId { get; set; } public required string Owner { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Cars/RegistrationChanged.cs b/src/Domain.Events.Shared/Cars/RegistrationChanged.cs index 79d62301..4dcee246 100644 --- a/src/Domain.Events.Shared/Cars/RegistrationChanged.cs +++ b/src/Domain.Events.Shared/Cars/RegistrationChanged.cs @@ -1,18 +1,26 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using Domain.Shared.Cars; +using JetBrains.Annotations; namespace Domain.Events.Shared.Cars; -public sealed class RegistrationChanged : IDomainEvent +public sealed class RegistrationChanged : DomainEvent { + public RegistrationChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public RegistrationChanged() + { + } + public required string Jurisdiction { get; set; } public required string Number { get; set; } public required string OrganizationId { get; set; } - public required string Status { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } + public required CarStatus Status { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Cars/UnavailabilitySlotAdded.cs b/src/Domain.Events.Shared/Cars/UnavailabilitySlotAdded.cs index 4621c9e4..f976514e 100644 --- a/src/Domain.Events.Shared/Cars/UnavailabilitySlotAdded.cs +++ b/src/Domain.Events.Shared/Cars/UnavailabilitySlotAdded.cs @@ -1,10 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; using Domain.Shared.Cars; +using JetBrains.Annotations; namespace Domain.Events.Shared.Cars; -public sealed class UnavailabilitySlotAdded : IDomainEvent +public sealed class UnavailabilitySlotAdded : DomainEvent { + public UnavailabilitySlotAdded(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public UnavailabilitySlotAdded() + { + } + public required UnavailabilityCausedBy CausedByReason { get; set; } public string? CausedByReference { get; set; } @@ -16,8 +27,4 @@ public sealed class UnavailabilitySlotAdded : IDomainEvent public required DateTime To { get; set; } public string? UnavailabilityId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Cars/UnavailabilitySlotRemoved.cs b/src/Domain.Events.Shared/Cars/UnavailabilitySlotRemoved.cs index 01c302b0..fd48cdf4 100644 --- a/src/Domain.Events.Shared/Cars/UnavailabilitySlotRemoved.cs +++ b/src/Domain.Events.Shared/Cars/UnavailabilitySlotRemoved.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Cars; -public sealed class UnavailabilitySlotRemoved : IDomainEvent +public sealed class UnavailabilitySlotRemoved : DomainEvent { - public required string OrganizationId { get; set; } + public UnavailabilitySlotRemoved(Identifier id) : base(id) + { + } - public required string UnavailabilityId { get; set; } + [UsedImplicitly] + public UnavailabilitySlotRemoved() + { + } - public required string RootId { get; set; } + public required string OrganizationId { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string UnavailabilityId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/Created.cs b/src/Domain.Events.Shared/EndUsers/Created.cs index 9c838918..6ffae9f3 100644 --- a/src/Domain.Events.Shared/EndUsers/Created.cs +++ b/src/Domain.Events.Shared/EndUsers/Created.cs @@ -1,16 +1,24 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using Domain.Shared.EndUsers; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string Access { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string Classification { get; set; } + [UsedImplicitly] + public Created() + { + } - public required string Status { get; set; } + public UserAccess Access { get; set; } - public required string RootId { get; set; } + public UserClassification Classification { get; set; } - public required DateTime OccurredUtc { get; set; } + public UserStatus Status { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/GuestInvitationAccepted.cs b/src/Domain.Events.Shared/EndUsers/GuestInvitationAccepted.cs index ba4dc3cb..482637e0 100644 --- a/src/Domain.Events.Shared/EndUsers/GuestInvitationAccepted.cs +++ b/src/Domain.Events.Shared/EndUsers/GuestInvitationAccepted.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class GuestInvitationAccepted : IDomainEvent +public sealed class GuestInvitationAccepted : DomainEvent { - public required DateTime AcceptedAtUtc { get; set; } + public GuestInvitationAccepted(Identifier id) : base(id) + { + } - public required string AcceptedEmailAddress { get; set; } + [UsedImplicitly] + public GuestInvitationAccepted() + { + } - public required string RootId { get; set; } + public required DateTime AcceptedAtUtc { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string AcceptedEmailAddress { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/GuestInvitationCreated.cs b/src/Domain.Events.Shared/EndUsers/GuestInvitationCreated.cs index aa2f8bec..e7b59801 100644 --- a/src/Domain.Events.Shared/EndUsers/GuestInvitationCreated.cs +++ b/src/Domain.Events.Shared/EndUsers/GuestInvitationCreated.cs @@ -1,16 +1,23 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class GuestInvitationCreated : IDomainEvent +public sealed class GuestInvitationCreated : DomainEvent { + public GuestInvitationCreated(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public GuestInvitationCreated() + { + } + public required string EmailAddress { get; set; } public required string InvitedById { get; set; } public required string Token { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/MembershipAdded.cs b/src/Domain.Events.Shared/EndUsers/MembershipAdded.cs index 5b677c78..5343c264 100644 --- a/src/Domain.Events.Shared/EndUsers/MembershipAdded.cs +++ b/src/Domain.Events.Shared/EndUsers/MembershipAdded.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class MembershipAdded : IDomainEvent +public sealed class MembershipAdded : DomainEvent { + public MembershipAdded(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public MembershipAdded() + { + } + public required List Features { get; set; } public required bool IsDefault { get; set; } @@ -13,8 +24,4 @@ public sealed class MembershipAdded : IDomainEvent public required string OrganizationId { get; set; } public required List Roles { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/MembershipDefaultChanged.cs b/src/Domain.Events.Shared/EndUsers/MembershipDefaultChanged.cs index 0c75f3e2..cae190b1 100644 --- a/src/Domain.Events.Shared/EndUsers/MembershipDefaultChanged.cs +++ b/src/Domain.Events.Shared/EndUsers/MembershipDefaultChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class MembershipDefaultChanged : IDomainEvent +public sealed class MembershipDefaultChanged : DomainEvent { - public required string FromMembershipId { get; set; } + public MembershipDefaultChanged(Identifier id) : base(id) + { + } - public required string ToMembershipId { get; set; } + [UsedImplicitly] + public MembershipDefaultChanged() + { + } - public required string RootId { get; set; } + public required string FromMembershipId { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string ToMembershipId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/MembershipFeatureAssigned.cs b/src/Domain.Events.Shared/EndUsers/MembershipFeatureAssigned.cs index b9a5cb73..dc97f9ad 100644 --- a/src/Domain.Events.Shared/EndUsers/MembershipFeatureAssigned.cs +++ b/src/Domain.Events.Shared/EndUsers/MembershipFeatureAssigned.cs @@ -1,16 +1,23 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class MembershipFeatureAssigned : IDomainEvent +public sealed class MembershipFeatureAssigned : DomainEvent { + public MembershipFeatureAssigned(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public MembershipFeatureAssigned() + { + } + public required string Feature { get; set; } public required string MembershipId { get; set; } public required string OrganizationId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/MembershipRoleAssigned.cs b/src/Domain.Events.Shared/EndUsers/MembershipRoleAssigned.cs index 81485346..df6531dd 100644 --- a/src/Domain.Events.Shared/EndUsers/MembershipRoleAssigned.cs +++ b/src/Domain.Events.Shared/EndUsers/MembershipRoleAssigned.cs @@ -1,16 +1,23 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class MembershipRoleAssigned : IDomainEvent +public sealed class MembershipRoleAssigned : DomainEvent { + public MembershipRoleAssigned(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public MembershipRoleAssigned() + { + } + public required string MembershipId { get; set; } public required string OrganizationId { get; set; } public required string Role { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/PlatformFeatureAssigned.cs b/src/Domain.Events.Shared/EndUsers/PlatformFeatureAssigned.cs index 1e953762..406a7c4c 100644 --- a/src/Domain.Events.Shared/EndUsers/PlatformFeatureAssigned.cs +++ b/src/Domain.Events.Shared/EndUsers/PlatformFeatureAssigned.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class PlatformFeatureAssigned : IDomainEvent +public sealed class PlatformFeatureAssigned : DomainEvent { - public required string Feature { get; set; } + public PlatformFeatureAssigned(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public PlatformFeatureAssigned() + { + } - public required DateTime OccurredUtc { get; set; } + public required string Feature { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/PlatformRoleAssigned.cs b/src/Domain.Events.Shared/EndUsers/PlatformRoleAssigned.cs index 41bdce85..c6a4f2da 100644 --- a/src/Domain.Events.Shared/EndUsers/PlatformRoleAssigned.cs +++ b/src/Domain.Events.Shared/EndUsers/PlatformRoleAssigned.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class PlatformRoleAssigned : IDomainEvent +public sealed class PlatformRoleAssigned : DomainEvent { - public required string Role { get; set; } + public PlatformRoleAssigned(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public PlatformRoleAssigned() + { + } - public required DateTime OccurredUtc { get; set; } + public required string Role { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/PlatformRoleUnassigned.cs b/src/Domain.Events.Shared/EndUsers/PlatformRoleUnassigned.cs index c535d3d0..840e6b0a 100644 --- a/src/Domain.Events.Shared/EndUsers/PlatformRoleUnassigned.cs +++ b/src/Domain.Events.Shared/EndUsers/PlatformRoleUnassigned.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class PlatformRoleUnassigned : IDomainEvent +public sealed class PlatformRoleUnassigned : DomainEvent { - public required string Role { get; set; } + public PlatformRoleUnassigned(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public PlatformRoleUnassigned() + { + } - public required DateTime OccurredUtc { get; set; } + public required string Role { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/EndUsers/Registered.cs b/src/Domain.Events.Shared/EndUsers/Registered.cs index 2304bf35..edbc21ae 100644 --- a/src/Domain.Events.Shared/EndUsers/Registered.cs +++ b/src/Domain.Events.Shared/EndUsers/Registered.cs @@ -1,22 +1,32 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using Domain.Shared.EndUsers; +using JetBrains.Annotations; namespace Domain.Events.Shared.EndUsers; -public sealed class Registered : IDomainEvent +public sealed class Registered : DomainEvent { - public required string Access { get; set; } + public Registered(Identifier id) : base(id) + { + } - public required string Classification { get; set; } + [UsedImplicitly] + public Registered() + { + } + + public UserAccess Access { get; set; } + + public UserClassification Classification { get; set; } public required List Features { get; set; } public required List Roles { get; set; } - public required string Status { get; set; } + public UserStatus Status { get; set; } public string? Username { get; set; } - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } + public required RegisteredUserProfile UserProfile { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/APIKeys/Created.cs b/src/Domain.Events.Shared/Identities/APIKeys/Created.cs index 79c344ea..a93fca10 100644 --- a/src/Domain.Events.Shared/Identities/APIKeys/Created.cs +++ b/src/Domain.Events.Shared/Identities/APIKeys/Created.cs @@ -1,16 +1,23 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.APIKeys; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { + public Created(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public Created() + { + } + public required string KeyHash { get; set; } public required string KeyToken { get; set; } public required string UserId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/APIKeys/KeyVerified.cs b/src/Domain.Events.Shared/Identities/APIKeys/KeyVerified.cs index 1919a516..e03106f9 100644 --- a/src/Domain.Events.Shared/Identities/APIKeys/KeyVerified.cs +++ b/src/Domain.Events.Shared/Identities/APIKeys/KeyVerified.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.APIKeys; -public sealed class KeyVerified : IDomainEvent +public sealed class KeyVerified : DomainEvent { - public required bool IsVerified { get; set; } + public KeyVerified(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public KeyVerified() + { + } - public required DateTime OccurredUtc { get; set; } + public required bool IsVerified { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/APIKeys/ParametersChanged.cs b/src/Domain.Events.Shared/Identities/APIKeys/ParametersChanged.cs index 6af5ffc7..5f85197e 100644 --- a/src/Domain.Events.Shared/Identities/APIKeys/ParametersChanged.cs +++ b/src/Domain.Events.Shared/Identities/APIKeys/ParametersChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.APIKeys; -public sealed class ParametersChanged : IDomainEvent +public sealed class ParametersChanged : DomainEvent { - public required string Description { get; set; } + public ParametersChanged(Identifier id) : base(id) + { + } - public required DateTime ExpiresOn { get; set; } + [UsedImplicitly] + public ParametersChanged() + { + } - public required string RootId { get; set; } + public required string Description { get; set; } - public required DateTime OccurredUtc { get; set; } + public required DateTime ExpiresOn { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/AuthTokens/Created.cs b/src/Domain.Events.Shared/Identities/AuthTokens/Created.cs index 5c8bd04f..ddefbc59 100644 --- a/src/Domain.Events.Shared/Identities/AuthTokens/Created.cs +++ b/src/Domain.Events.Shared/Identities/AuthTokens/Created.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.AuthTokens; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string UserId { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public Created() + { + } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/AuthTokens/TokensChanged.cs b/src/Domain.Events.Shared/Identities/AuthTokens/TokensChanged.cs index 3df89691..343c9600 100644 --- a/src/Domain.Events.Shared/Identities/AuthTokens/TokensChanged.cs +++ b/src/Domain.Events.Shared/Identities/AuthTokens/TokensChanged.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.AuthTokens; -public sealed class TokensChanged : IDomainEvent +public sealed class TokensChanged : DomainEvent { + public TokensChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public TokensChanged() + { + } + public required string AccessToken { get; set; } public required DateTime AccessTokenExpiresOn { get; set; } @@ -13,8 +24,4 @@ public sealed class TokensChanged : IDomainEvent public required DateTime RefreshTokenExpiresOn { get; set; } public required string UserId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/AuthTokens/TokensRefreshed.cs b/src/Domain.Events.Shared/Identities/AuthTokens/TokensRefreshed.cs index 654b018a..f95bd49b 100644 --- a/src/Domain.Events.Shared/Identities/AuthTokens/TokensRefreshed.cs +++ b/src/Domain.Events.Shared/Identities/AuthTokens/TokensRefreshed.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.AuthTokens; -public sealed class TokensRefreshed : IDomainEvent +public sealed class TokensRefreshed : DomainEvent { + public TokensRefreshed(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public TokensRefreshed() + { + } + public required string AccessToken { get; set; } public required DateTime AccessTokenExpiresOn { get; set; } @@ -13,8 +24,4 @@ public sealed class TokensRefreshed : IDomainEvent public required DateTime RefreshTokenExpiresOn { get; set; } public required string UserId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/AuthTokens/TokensRevoked.cs b/src/Domain.Events.Shared/Identities/AuthTokens/TokensRevoked.cs index e68bcfa3..f3d23bc7 100644 --- a/src/Domain.Events.Shared/Identities/AuthTokens/TokensRevoked.cs +++ b/src/Domain.Events.Shared/Identities/AuthTokens/TokensRevoked.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.AuthTokens; -public sealed class TokensRevoked : IDomainEvent +public sealed class TokensRevoked : DomainEvent { - public required string UserId { get; set; } + public TokensRevoked(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public TokensRevoked() + { + } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountLocked.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountLocked.cs index 31ebbb83..c95df9dc 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountLocked.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountLocked.cs @@ -1,10 +1,17 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class AccountLocked : IDomainEvent +public sealed class AccountLocked : DomainEvent { - public required string RootId { get; set; } + public AccountLocked(Identifier id) : base(id) + { + } - public required DateTime OccurredUtc { get; set; } + [UsedImplicitly] + public AccountLocked() + { + } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountUnlocked.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountUnlocked.cs index 31dc3fb9..6ba45382 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountUnlocked.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/AccountUnlocked.cs @@ -1,10 +1,17 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class AccountUnlocked : IDomainEvent +public sealed class AccountUnlocked : DomainEvent { - public required string RootId { get; set; } + public AccountUnlocked(Identifier id) : base(id) + { + } - public required DateTime OccurredUtc { get; set; } + [UsedImplicitly] + public AccountUnlocked() + { + } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/Created.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/Created.cs index bd5c3c33..f89a6ae9 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/Created.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/Created.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string UserId { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public Created() + { + } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/CredentialsChanged.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/CredentialsChanged.cs index b4e8802e..aff9c681 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/CredentialsChanged.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/CredentialsChanged.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class CredentialsChanged : IDomainEvent +public sealed class CredentialsChanged : DomainEvent { - public required string PasswordHash { get; set; } + public CredentialsChanged(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public CredentialsChanged() + { + } - public required DateTime OccurredUtc { get; set; } + public required string PasswordHash { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetCompleted.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetCompleted.cs index c13a466c..c1052f60 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetCompleted.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetCompleted.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class PasswordResetCompleted : IDomainEvent +public sealed class PasswordResetCompleted : DomainEvent { - public required string PasswordHash { get; set; } + public PasswordResetCompleted(Identifier id) : base(id) + { + } - public required string Token { get; set; } + [UsedImplicitly] + public PasswordResetCompleted() + { + } - public required string RootId { get; set; } + public required string PasswordHash { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string Token { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetInitiated.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetInitiated.cs index cdab4996..6c515124 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetInitiated.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordResetInitiated.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class PasswordResetInitiated : IDomainEvent +public sealed class PasswordResetInitiated : DomainEvent { - public required string Token { get; set; } + public PasswordResetInitiated(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public PasswordResetInitiated() + { + } - public required DateTime OccurredUtc { get; set; } + public required string Token { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordVerified.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordVerified.cs index 7033d830..c436b11d 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordVerified.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/PasswordVerified.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class PasswordVerified : IDomainEvent +public sealed class PasswordVerified : DomainEvent { - public required bool AuditAttempt { get; set; } + public PasswordVerified(Identifier id) : base(id) + { + } - public required bool IsVerified { get; set; } + [UsedImplicitly] + public PasswordVerified() + { + } - public required string RootId { get; set; } + public required bool AuditAttempt { get; set; } - public required DateTime OccurredUtc { get; set; } + public required bool IsVerified { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationChanged.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationChanged.cs index 1d339d5e..cd3e63b7 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationChanged.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class RegistrationChanged : IDomainEvent +public sealed class RegistrationChanged : DomainEvent { - public required string EmailAddress { get; set; } + public RegistrationChanged(Identifier id) : base(id) + { + } - public required string Name { get; set; } + [UsedImplicitly] + public RegistrationChanged() + { + } - public required string RootId { get; set; } + public required string EmailAddress { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string Name { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationCreated.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationCreated.cs index 55a46fc0..db24ffa9 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationCreated.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationCreated.cs @@ -1,12 +1,19 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class RegistrationVerificationCreated : IDomainEvent +public sealed class RegistrationVerificationCreated : DomainEvent { - public required string Token { get; set; } + public RegistrationVerificationCreated(Identifier id) : base(id) + { + } - public required string RootId { get; set; } + [UsedImplicitly] + public RegistrationVerificationCreated() + { + } - public required DateTime OccurredUtc { get; set; } + public required string Token { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationVerified.cs b/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationVerified.cs index dfbec156..f74c31db 100644 --- a/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationVerified.cs +++ b/src/Domain.Events.Shared/Identities/PasswordCredentials/RegistrationVerificationVerified.cs @@ -1,10 +1,17 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.PasswordCredentials; -public sealed class RegistrationVerificationVerified : IDomainEvent +public sealed class RegistrationVerificationVerified : DomainEvent { - public required string RootId { get; set; } + public RegistrationVerificationVerified(Identifier id) : base(id) + { + } - public required DateTime OccurredUtc { get; set; } + [UsedImplicitly] + public RegistrationVerificationVerified() + { + } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/SSOUsers/Created.cs b/src/Domain.Events.Shared/Identities/SSOUsers/Created.cs index b7956520..e345f216 100644 --- a/src/Domain.Events.Shared/Identities/SSOUsers/Created.cs +++ b/src/Domain.Events.Shared/Identities/SSOUsers/Created.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.SSOUsers; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string ProviderName { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string UserId { get; set; } + [UsedImplicitly] + public Created() + { + } - public required string RootId { get; set; } + public required string ProviderName { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Identities/SSOUsers/TokensUpdated.cs b/src/Domain.Events.Shared/Identities/SSOUsers/TokensUpdated.cs index 2136f743..8bb8457a 100644 --- a/src/Domain.Events.Shared/Identities/SSOUsers/TokensUpdated.cs +++ b/src/Domain.Events.Shared/Identities/SSOUsers/TokensUpdated.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.Identities.SSOUsers; -public sealed class TokensUpdated : IDomainEvent +public sealed class TokensUpdated : DomainEvent { + public TokensUpdated(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public TokensUpdated() + { + } + public required string CountryCode { get; set; } public required string EmailAddress { get; set; } @@ -15,8 +26,4 @@ public sealed class TokensUpdated : IDomainEvent public required string Timezone { get; set; } public required string Tokens { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Organizations/Created.cs b/src/Domain.Events.Shared/Organizations/Created.cs index 7bd9907d..4aa0f376 100644 --- a/src/Domain.Events.Shared/Organizations/Created.cs +++ b/src/Domain.Events.Shared/Organizations/Created.cs @@ -1,17 +1,24 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; using Domain.Shared.Organizations; +using JetBrains.Annotations; namespace Domain.Events.Shared.Organizations; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { - public required string CreatedById { get; set; } + public Created(Identifier id) : base(id) + { + } - public required string Name { get; set; } + [UsedImplicitly] + public Created() + { + } - public required OrganizationOwnership Ownership { get; set; } + public required string CreatedById { get; set; } - public required string RootId { get; set; } + public required string Name { get; set; } - public required DateTime OccurredUtc { get; set; } + public OrganizationOwnership Ownership { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Organizations/SettingCreated.cs b/src/Domain.Events.Shared/Organizations/SettingCreated.cs index 61e40d02..61fd4efd 100644 --- a/src/Domain.Events.Shared/Organizations/SettingCreated.cs +++ b/src/Domain.Events.Shared/Organizations/SettingCreated.cs @@ -1,10 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; using Domain.Shared.Organizations; +using JetBrains.Annotations; namespace Domain.Events.Shared.Organizations; -public sealed class SettingCreated : IDomainEvent +public sealed class SettingCreated : DomainEvent { + public SettingCreated(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public SettingCreated() + { + } + public required bool IsEncrypted { get; set; } public required string Name { get; set; } @@ -12,8 +23,4 @@ public sealed class SettingCreated : IDomainEvent public required string StringValue { get; set; } public required SettingValueType ValueType { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/Organizations/SettingUpdated.cs b/src/Domain.Events.Shared/Organizations/SettingUpdated.cs index c8a5c299..250641d1 100644 --- a/src/Domain.Events.Shared/Organizations/SettingUpdated.cs +++ b/src/Domain.Events.Shared/Organizations/SettingUpdated.cs @@ -1,10 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; using Domain.Shared.Organizations; +using JetBrains.Annotations; namespace Domain.Events.Shared.Organizations; -public sealed class SettingUpdated : IDomainEvent +public sealed class SettingUpdated : DomainEvent { + public SettingUpdated(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public SettingUpdated() + { + } + public required string From { get; set; } public required SettingValueType FromType { get; set; } @@ -16,8 +27,4 @@ public sealed class SettingUpdated : IDomainEvent public required string To { get; set; } public required SettingValueType ToType { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/ContactAddressChanged.cs b/src/Domain.Events.Shared/UserProfiles/ContactAddressChanged.cs index 8f79a4fc..2ff5db71 100644 --- a/src/Domain.Events.Shared/UserProfiles/ContactAddressChanged.cs +++ b/src/Domain.Events.Shared/UserProfiles/ContactAddressChanged.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class ContactAddressChanged : IDomainEvent +public sealed class ContactAddressChanged : DomainEvent { + public ContactAddressChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public ContactAddressChanged() + { + } + public string? City { get; set; } public required string CountryCode { get; set; } @@ -19,8 +30,4 @@ public sealed class ContactAddressChanged : IDomainEvent public required string UserId { get; set; } public string? Zip { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/Created.cs b/src/Domain.Events.Shared/UserProfiles/Created.cs index f28d432e..c8b35d00 100644 --- a/src/Domain.Events.Shared/UserProfiles/Created.cs +++ b/src/Domain.Events.Shared/UserProfiles/Created.cs @@ -1,9 +1,20 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class Created : IDomainEvent +public sealed class Created : DomainEvent { + public Created(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public Created() + { + } + public required string DisplayName { get; set; } public required string FirstName { get; set; } @@ -13,8 +24,4 @@ public sealed class Created : IDomainEvent public required string Type { get; set; } public required string UserId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/DisplayNameChanged.cs b/src/Domain.Events.Shared/UserProfiles/DisplayNameChanged.cs index fc23dadf..7940c3a1 100644 --- a/src/Domain.Events.Shared/UserProfiles/DisplayNameChanged.cs +++ b/src/Domain.Events.Shared/UserProfiles/DisplayNameChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class DisplayNameChanged : IDomainEvent +public sealed class DisplayNameChanged : DomainEvent { - public required string DisplayName { get; set; } + public DisplayNameChanged(Identifier id) : base(id) + { + } - public required string UserId { get; set; } + [UsedImplicitly] + public DisplayNameChanged() + { + } - public required string RootId { get; set; } + public required string DisplayName { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/EmailAddressChanged.cs b/src/Domain.Events.Shared/UserProfiles/EmailAddressChanged.cs index 4c8910fa..8e7d4ede 100644 --- a/src/Domain.Events.Shared/UserProfiles/EmailAddressChanged.cs +++ b/src/Domain.Events.Shared/UserProfiles/EmailAddressChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class EmailAddressChanged : IDomainEvent +public sealed class EmailAddressChanged : DomainEvent { - public required string EmailAddress { get; set; } + public EmailAddressChanged(Identifier id) : base(id) + { + } - public required string UserId { get; set; } + [UsedImplicitly] + public EmailAddressChanged() + { + } - public required string RootId { get; set; } + public required string EmailAddress { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/NameChanged.cs b/src/Domain.Events.Shared/UserProfiles/NameChanged.cs index 24911fd2..a3e3bae9 100644 --- a/src/Domain.Events.Shared/UserProfiles/NameChanged.cs +++ b/src/Domain.Events.Shared/UserProfiles/NameChanged.cs @@ -1,16 +1,23 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class NameChanged : IDomainEvent +public sealed class NameChanged : DomainEvent { + public NameChanged(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public NameChanged() + { + } + public required string FirstName { get; set; } public string? LastName { get; set; } public required string UserId { get; set; } - - public required string RootId { get; set; } - - public required DateTime OccurredUtc { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/PhoneNumberChanged.cs b/src/Domain.Events.Shared/UserProfiles/PhoneNumberChanged.cs index 9a1d92a3..177630b0 100644 --- a/src/Domain.Events.Shared/UserProfiles/PhoneNumberChanged.cs +++ b/src/Domain.Events.Shared/UserProfiles/PhoneNumberChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class PhoneNumberChanged : IDomainEvent +public sealed class PhoneNumberChanged : DomainEvent { - public required string Number { get; set; } + public PhoneNumberChanged(Identifier id) : base(id) + { + } - public required string UserId { get; set; } + [UsedImplicitly] + public PhoneNumberChanged() + { + } - public required string RootId { get; set; } + public required string Number { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Events.Shared/UserProfiles/TimezoneChanged.cs b/src/Domain.Events.Shared/UserProfiles/TimezoneChanged.cs index 1d4cdb23..98b1eea1 100644 --- a/src/Domain.Events.Shared/UserProfiles/TimezoneChanged.cs +++ b/src/Domain.Events.Shared/UserProfiles/TimezoneChanged.cs @@ -1,14 +1,21 @@ -using Domain.Interfaces.Entities; +using Domain.Common; +using Domain.Common.ValueObjects; +using JetBrains.Annotations; namespace Domain.Events.Shared.UserProfiles; -public sealed class TimezoneChanged : IDomainEvent +public sealed class TimezoneChanged : DomainEvent { - public required string Timezone { get; set; } + public TimezoneChanged(Identifier id) : base(id) + { + } - public required string UserId { get; set; } + [UsedImplicitly] + public TimezoneChanged() + { + } - public required string RootId { get; set; } + public required string Timezone { get; set; } - public required DateTime OccurredUtc { get; set; } + public required string UserId { get; set; } } \ No newline at end of file diff --git a/src/Domain.Interfaces/Entities/IDomainEvent.cs b/src/Domain.Interfaces/Entities/IDomainEvent.cs index 709a280e..47c5d5d3 100644 --- a/src/Domain.Interfaces/Entities/IDomainEvent.cs +++ b/src/Domain.Interfaces/Entities/IDomainEvent.cs @@ -1,7 +1,7 @@ namespace Domain.Interfaces.Entities; /// -/// Defines a domain event to communicate past events of an aggregate +/// Defines a domain event to communicate past events of an aggregate within the same bounded context /// public interface IDomainEvent { diff --git a/src/CarsDomain/CarStatus.cs b/src/Domain.Shared/Cars/CarStatus.cs similarity index 69% rename from src/CarsDomain/CarStatus.cs rename to src/Domain.Shared/Cars/CarStatus.cs index 2be3bf11..b4dd5597 100644 --- a/src/CarsDomain/CarStatus.cs +++ b/src/Domain.Shared/Cars/CarStatus.cs @@ -1,4 +1,4 @@ -namespace CarsDomain; +namespace Domain.Shared.Cars; public enum CarStatus { diff --git a/src/Domain.Shared/EndUsers/RegisteredUserProfile.cs b/src/Domain.Shared/EndUsers/RegisteredUserProfile.cs new file mode 100644 index 00000000..07d6face --- /dev/null +++ b/src/Domain.Shared/EndUsers/RegisteredUserProfile.cs @@ -0,0 +1,12 @@ +namespace Domain.Shared.EndUsers; + +public class RegisteredUserProfile +{ + public required string CountryCode { get; set; } + + public required string FirstName { get; set; } + + public string? LastName { get; set; } + + public required string Timezone { get; set; } +} \ No newline at end of file diff --git a/src/Domain.Shared/EndUsers/UserAccess.cs b/src/Domain.Shared/EndUsers/UserAccess.cs new file mode 100644 index 00000000..2fa5df9e --- /dev/null +++ b/src/Domain.Shared/EndUsers/UserAccess.cs @@ -0,0 +1,7 @@ +namespace Domain.Shared.EndUsers; + +public enum UserAccess +{ + Enabled = 0, + Suspended = 1 // Cannot access the account anymore +} \ No newline at end of file diff --git a/src/EndUsersDomain/UserClassification.cs b/src/Domain.Shared/EndUsers/UserClassification.cs similarity index 80% rename from src/EndUsersDomain/UserClassification.cs rename to src/Domain.Shared/EndUsers/UserClassification.cs index f45f1f40..656481d3 100644 --- a/src/EndUsersDomain/UserClassification.cs +++ b/src/Domain.Shared/EndUsers/UserClassification.cs @@ -1,4 +1,4 @@ -namespace EndUsersDomain; +namespace Domain.Shared.EndUsers; public enum UserClassification { diff --git a/src/Domain.Shared/EndUsers/UserStatus.cs b/src/Domain.Shared/EndUsers/UserStatus.cs new file mode 100644 index 00000000..4f6d95dc --- /dev/null +++ b/src/Domain.Shared/EndUsers/UserStatus.cs @@ -0,0 +1,7 @@ +namespace Domain.Shared.EndUsers; + +public enum UserStatus +{ + Unregistered = 0, // An invited guest + Registered = 1 +} \ No newline at end of file diff --git a/src/EndUsersApplication.UnitTests/EndUsersApplication.DomainEventHandlersSpec.cs b/src/EndUsersApplication.UnitTests/EndUsersApplication.DomainEventHandlersSpec.cs new file mode 100644 index 00000000..69b1ab7e --- /dev/null +++ b/src/EndUsersApplication.UnitTests/EndUsersApplication.DomainEventHandlersSpec.cs @@ -0,0 +1,100 @@ +using Application.Interfaces; +using Application.Services.Shared; +using Common; +using Common.Configuration; +using Domain.Common.Identity; +using Domain.Common.ValueObjects; +using Domain.Interfaces.Authorization; +using Domain.Interfaces.Entities; +using Domain.Shared; +using Domain.Shared.EndUsers; +using Domain.Shared.Organizations; +using EndUsersApplication.Persistence; +using EndUsersDomain; +using Moq; +using OrganizationsDomain; +using UnitTesting.Common; +using Xunit; +using Events = OrganizationsDomain.Events; +using Membership = EndUsersDomain.Membership; + +namespace EndUsersApplication.UnitTests; + +[Trait("Category", "Unit")] +public class EndUsersApplicationDomainEventHandlersSpec +{ + private readonly EndUsersApplication _application; + private readonly Mock _caller; + private readonly Mock _endUserRepository; + private readonly Mock _idFactory; + private readonly Mock _recorder; + + public EndUsersApplicationDomainEventHandlersSpec() + { + _recorder = new Mock(); + _caller = new Mock(); + _idFactory = new Mock(); + var membershipCounter = 0; + _idFactory.Setup(idf => idf.Create(It.IsAny())) + .Returns((IIdentifiableEntity entity) => + { + if (entity is Membership) + { + return $"amembershipid{membershipCounter++}".ToId(); + } + + return "anid".ToId(); + }); + var settings = new Mock(); + settings.Setup( + s => s.Platform.GetString(EndUsersApplication.PermittedOperatorsSettingName, It.IsAny())) + .Returns(""); + _endUserRepository = new Mock(); + _endUserRepository.Setup(rep => rep.SaveAsync(It.IsAny(), It.IsAny())) + .Returns((EndUserRoot root, CancellationToken _) => Task.FromResult>(root)); + var invitationRepository = new Mock(); + var userProfilesService = new Mock(); + var notificationsService = new Mock(); + + _application = + new EndUsersApplication(_recorder.Object, _idFactory.Object, settings.Object, notificationsService.Object, + userProfilesService.Object, invitationRepository.Object, _endUserRepository.Object); + } + + [Fact] + public async Task WhenHandleOrganizationCreatedAsyncAndUserNoExist_ThenReturnsError() + { + _endUserRepository.Setup(rep => rep.LoadAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Error.EntityNotFound()); + var domainEvent = Events.Created("anorganizationid".ToId(), OrganizationOwnership.Shared, + "auserid".ToId(), DisplayName.Create("adisplayname").Value); + + var result = + await _application.HandleOrganizationCreatedAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeError(ErrorCode.EntityNotFound); + } + + [Fact] + public async Task HandleOrganizationCreatedAsync_ThenAddsMembership() + { + var user = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; + user.Register(Roles.Create(PlatformRoles.Standard).Value, Features.Create(PlatformFeatures.Basic).Value, + EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); + _endUserRepository.Setup(rep => rep.LoadAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(user); + var domainEvent = Events.Created("anorganizationid".ToId(), OrganizationOwnership.Shared, + "auserid".ToId(), DisplayName.Create("adisplayname").Value); + + var result = + await _application.HandleOrganizationCreatedAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeSuccess(); + _endUserRepository.Verify(rep => rep.SaveAsync(It.Is(eu => + eu.Memberships[0].IsDefault + && eu.Memberships[0].OrganizationId == "anorganizationid".ToId() + && eu.Memberships[0].Roles.HasRole(TenantRoles.Member) + && eu.Memberships[0].Features.HasFeature(TenantFeatures.Basic) + ), It.IsAny())); + } +} \ No newline at end of file diff --git a/src/EndUsersApplication.UnitTests/EndUsersApplication.UnitTests.csproj b/src/EndUsersApplication.UnitTests/EndUsersApplication.UnitTests.csproj index 5253c48e..5479e247 100644 --- a/src/EndUsersApplication.UnitTests/EndUsersApplication.UnitTests.csproj +++ b/src/EndUsersApplication.UnitTests/EndUsersApplication.UnitTests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/EndUsersApplication.UnitTests/EndUsersApplicationSpec.cs b/src/EndUsersApplication.UnitTests/EndUsersApplicationSpec.cs index 4f3abc30..20e102b2 100644 --- a/src/EndUsersApplication.UnitTests/EndUsersApplicationSpec.cs +++ b/src/EndUsersApplication.UnitTests/EndUsersApplicationSpec.cs @@ -10,6 +10,7 @@ using Domain.Interfaces.Entities; using Domain.Services.Shared.DomainServices; using Domain.Shared; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence; using EndUsersDomain; using FluentAssertions; @@ -30,7 +31,6 @@ public class EndUsersApplicationSpec private readonly Mock _idFactory; private readonly Mock _invitationRepository; private readonly Mock _notificationsService; - private readonly Mock _organizationsService; private readonly Mock _recorder; private readonly Mock _userProfilesService; @@ -56,24 +56,17 @@ public EndUsersApplicationSpec() .Returns(""); _endUserRepository = new Mock(); _endUserRepository.Setup(rep => rep.SaveAsync(It.IsAny(), It.IsAny())) - .Returns((EndUserRoot root, CancellationToken _) => Task.FromResult>(root)); + .ReturnsAsync((EndUserRoot root, CancellationToken _) => root); + _endUserRepository.Setup(rep => + rep.SaveAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((EndUserRoot root, bool _, CancellationToken _) => root); _invitationRepository = new Mock(); - _organizationsService = new Mock(); - _organizationsService.Setup(os => os.CreateOrganizationPrivateAsync(It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny())) - .ReturnsAsync(new Organization - { - Id = "anorganizationid", - CreatedById = "auserid", - Name = "aname" - }); _userProfilesService = new Mock(); _notificationsService = new Mock(); _application = new EndUsersApplication(_recorder.Object, _idFactory.Object, settings.Object, - _notificationsService.Object, _organizationsService.Object, _userProfilesService.Object, + _notificationsService.Object, _userProfilesService.Object, _invitationRepository.Object, _endUserRepository.Object); } @@ -109,6 +102,8 @@ public async Task WhenRegisterPersonAsyncAndNotAcceptedTerms_ThenReturnsError() [Fact] public async Task WhenRegisterPersonAsyncAndWasInvitedAsGuest_ThenCompletesRegistration() { + _caller.Setup(cc => cc.CallId) + .Returns("acallid"); var invitee = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; _userProfilesService.Setup(ups => ups.FindPersonByEmailAddressPrivateAsync(It.IsAny(), It.IsAny(), @@ -118,8 +113,7 @@ public async Task WhenRegisterPersonAsyncAndWasInvitedAsGuest_ThenCompletesRegis rep.FindInvitedGuestByEmailAddressAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(invitee.ToOptional()); _userProfilesService.Setup(ups => - ups.CreatePersonProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new UserProfile { @@ -139,6 +133,14 @@ public async Task WhenRegisterPersonAsyncAndWasInvitedAsGuest_ThenCompletesRegis CountryCode = "acountrycode" } }); + _endUserRepository.Setup(rep => + rep.SaveAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((EndUserRoot root, bool _, CancellationToken _) => + { + // HACK: By this time, domain events have created the default membership + root.AddMembership("anorganizationid".ToId(), Roles.Empty, Features.Empty); + return root; + }); var result = await _application.RegisterPersonAsync(_caller.Object, null, "auser@company.com", "afirstname", @@ -161,13 +163,11 @@ public async Task WhenRegisterPersonAsyncAndWasInvitedAsGuest_ThenCompletesRegis result.Value.Profile.Timezone.Should().Be("atimezone"); _invitationRepository.Verify(rep => rep.FindInvitedGuestByTokenAsync(It.IsAny(), It.IsAny()), Times.Never); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, "anid", "afirstname alastname", - OrganizationOwnership.Personal, - It.IsAny())); _userProfilesService.Verify(ups => - ups.CreatePersonProfilePrivateAsync(_caller.Object, "anid", "auser@company.com", "afirstname", "alastname", - null, null, It.IsAny())); + ups.GetProfilePrivateAsync(It.Is(cc => + cc.CallId == "acallid" + && cc.IsServiceAccount + ), "anid", It.IsAny())); _notificationsService.Verify(ns => ns.NotifyReRegistrationCourtesyAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -179,6 +179,8 @@ public async Task WhenRegisterPersonAsyncAndAcceptingGuestInvitation_ThenComplet { _caller.Setup(cc => cc.CallerId) .Returns(CallerConstants.AnonymousUserId); + _caller.Setup(cc => cc.CallId) + .Returns("acallid"); var tokensService = new Mock(); tokensService.Setup(ts => ts.CreateGuestInvitationToken()) .Returns("aninvitationtoken"); @@ -200,8 +202,7 @@ await invitee.InviteGuestAsync(tokensService.Object, "aninviterid".ToId(), rep.FindInvitedGuestByEmailAddressAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(invitee.ToOptional()); _userProfilesService.Setup(ups => - ups.CreatePersonProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new UserProfile { @@ -221,6 +222,14 @@ await invitee.InviteGuestAsync(tokensService.Object, "aninviterid".ToId(), CountryCode = "acountrycode" } }); + _endUserRepository.Setup(rep => + rep.SaveAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((EndUserRoot root, bool _, CancellationToken _) => + { + // HACK: By this time, domain events have created the default membership + root.AddMembership("anorganizationid".ToId(), Roles.Empty, Features.Empty); + return root; + }); var result = await _application.RegisterPersonAsync(_caller.Object, "aninvitationtoken", "auser@company.com", "afirstname", "alastname", null, null, true, CancellationToken.None); @@ -242,13 +251,11 @@ await invitee.InviteGuestAsync(tokensService.Object, "aninviterid".ToId(), result.Value.Profile.Timezone.Should().Be("atimezone"); _invitationRepository.Verify(rep => rep.FindInvitedGuestByTokenAsync("aninvitationtoken", It.IsAny())); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, "anid", "afirstname alastname", - OrganizationOwnership.Personal, - It.IsAny())); _userProfilesService.Verify(ups => - ups.CreatePersonProfilePrivateAsync(_caller.Object, "anid", "auser@company.com", "afirstname", "alastname", - null, null, It.IsAny())); + ups.GetProfilePrivateAsync(It.Is(cc => + cc.CallId == "acallid" + && cc.IsServiceAccount + ), "anid", It.IsAny())); _notificationsService.Verify(ns => ns.NotifyReRegistrationCourtesyAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -258,6 +265,8 @@ await invitee.InviteGuestAsync(tokensService.Object, "aninviterid".ToId(), [Fact] public async Task WhenRegisterPersonAsyncAndAcceptingAnUnknownInvitation_ThenRegisters() { + _caller.Setup(cc => cc.CallId) + .Returns("acallid"); _invitationRepository.Setup(rep => rep.FindInvitedGuestByTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(Optional.None); @@ -270,8 +279,7 @@ public async Task WhenRegisterPersonAsyncAndAcceptingAnUnknownInvitation_ThenReg rep.FindInvitedGuestByEmailAddressAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(invitee.ToOptional()); _userProfilesService.Setup(ups => - ups.CreatePersonProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new UserProfile { @@ -291,11 +299,17 @@ public async Task WhenRegisterPersonAsyncAndAcceptingAnUnknownInvitation_ThenReg CountryCode = "acountrycode" } }); + _endUserRepository.Setup(rep => + rep.SaveAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((EndUserRoot root, bool _, CancellationToken _) => + { + // HACK: By this time, domain events have created the default membership + root.AddMembership("anorganizationid".ToId(), Roles.Empty, Features.Empty); + return root; + }); var result = await _application.RegisterPersonAsync(_caller.Object, "anunknowninvitationtoken", - "auser@company.com", - "afirstname", - "alastname", null, null, true, CancellationToken.None); + "auser@company.com", "afirstname", "alastname", null, null, true, CancellationToken.None); result.Should().BeSuccess(); result.Value.Id.Should().Be("anid"); @@ -314,13 +328,11 @@ public async Task WhenRegisterPersonAsyncAndAcceptingAnUnknownInvitation_ThenReg result.Value.Profile.Timezone.Should().Be("atimezone"); _invitationRepository.Verify(rep => rep.FindInvitedGuestByTokenAsync("anunknowninvitationtoken", It.IsAny())); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, "anid", "afirstname alastname", - OrganizationOwnership.Personal, - It.IsAny())); _userProfilesService.Verify(ups => - ups.CreatePersonProfilePrivateAsync(_caller.Object, "anid", "auser@company.com", "afirstname", "alastname", - null, null, It.IsAny())); + ups.GetProfilePrivateAsync(It.Is(cc => + cc.CallId == "acallid" + && cc.IsServiceAccount + ), "anid", It.IsAny())); _notificationsService.Verify(ns => ns.NotifyReRegistrationCourtesyAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -370,13 +382,10 @@ public async Task _userProfilesService.Setup(ups => ups.FindPersonByEmailAddressPrivateAsync(_caller.Object, "auser@company.com", It.IsAny())); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny()), Times.Never); _userProfilesService.Verify(ups => - ups.CreatePersonProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny()), Times.Never); + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), + It.IsAny()), + Times.Never); _notificationsService.Verify( ns => ns.NotifyReRegistrationCourtesyAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -387,7 +396,8 @@ public async Task public async Task WhenRegisterPersonAsyncAndAlreadyRegistered_ThenSendsCourtesyEmail() { var endUser = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; - endUser.Register(Roles.Empty, Features.Empty, EmailAddress.Create("auser@company.com").Value); + endUser.Register(Roles.Empty, Features.Empty, EndUserProfile.Create("afirstname").Value, + EmailAddress.Create("auser@company.com").Value); _userProfilesService.Setup(ups => ups.FindPersonByEmailAddressPrivateAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -440,13 +450,10 @@ public async Task WhenRegisterPersonAsyncAndAlreadyRegistered_ThenSendsCourtesyE result.Value.Profile.Timezone.Should().Be("atimezone"); _invitationRepository.Verify(rep => rep.FindInvitedGuestByTokenAsync(It.IsAny(), It.IsAny()), Times.Never); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny()), Times.Never); _userProfilesService.Verify(ups => - ups.CreatePersonProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny()), Times.Never); + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), + It.IsAny()), + Times.Never); _notificationsService.Verify(ns => ns.NotifyReRegistrationCourtesyAsync(_caller.Object, "anid", "anotheruser@company.com", "afirstname", "atimezone", "acountrycode", CancellationToken.None)); } @@ -454,6 +461,8 @@ public async Task WhenRegisterPersonAsyncAndAlreadyRegistered_ThenSendsCourtesyE [Fact] public async Task WhenRegisterPersonAsyncAndNeverRegisteredNorInvitedAsGuest_ThenRegisters() { + _caller.Setup(cc => cc.CallId) + .Returns("acallid"); _userProfilesService.Setup(ups => ups.FindPersonByEmailAddressPrivateAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -462,8 +471,7 @@ public async Task WhenRegisterPersonAsyncAndNeverRegisteredNorInvitedAsGuest_The rep.FindInvitedGuestByEmailAddressAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(Optional.None); _userProfilesService.Setup(ups => - ups.CreatePersonProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new UserProfile { @@ -483,6 +491,14 @@ public async Task WhenRegisterPersonAsyncAndNeverRegisteredNorInvitedAsGuest_The CountryCode = "acountrycode" } }); + _endUserRepository.Setup(rep => + rep.SaveAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((EndUserRoot root, bool _, CancellationToken _) => + { + // HACK: By this time, domain events have created the default membership + root.AddMembership("anorganizationid".ToId(), Roles.Empty, Features.Empty); + return root; + }); var result = await _application.RegisterPersonAsync(_caller.Object, null, "auser@company.com", "afirstname", @@ -505,21 +521,19 @@ public async Task WhenRegisterPersonAsyncAndNeverRegisteredNorInvitedAsGuest_The result.Value.Profile.Timezone.Should().Be("atimezone"); _invitationRepository.Verify(rep => rep.FindInvitedGuestByTokenAsync(It.IsAny(), It.IsAny()), Times.Never); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, "anid", "afirstname alastname", - OrganizationOwnership.Personal, - It.IsAny())); _userProfilesService.Verify(ups => - ups.CreatePersonProfilePrivateAsync(_caller.Object, "anid", "auser@company.com", "afirstname", "alastname", - null, null, It.IsAny())); + ups.GetProfilePrivateAsync(It.Is(cc => + cc.CallId == "acallid" + && cc.IsServiceAccount + ), "anid", It.IsAny())); } [Fact] public async Task WhenRegisterMachineAsyncByAnonymousUser_ThenRegistersWithNoFeatures() { _userProfilesService.Setup(ups => - ups.CreateMachineProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), + It.IsAny())) .ReturnsAsync(new UserProfile { Id = "aprofileid", @@ -538,6 +552,16 @@ public async Task WhenRegisterMachineAsyncByAnonymousUser_ThenRegistersWithNoFea }); _caller.Setup(cc => cc.IsAuthenticated) .Returns(false); + _caller.Setup(cc => cc.CallId) + .Returns("acallid"); + _endUserRepository.Setup(rep => + rep.SaveAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((EndUserRoot root, bool _, CancellationToken _) => + { + // HACK: By this time, domain events have created the default membership + root.AddMembership("anorganizationid".ToId(), Roles.Empty, Features.Empty); + return root; + }); var result = await _application.RegisterMachineAsync(_caller.Object, "aname", Timezones.Default.ToString(), CountryCodes.Default.ToString(), CancellationToken.None); @@ -557,13 +581,11 @@ public async Task WhenRegisterMachineAsyncByAnonymousUser_ThenRegistersWithNoFea result.Value.Profile.DisplayName.Should().Be("amachinename"); result.Value.Profile.EmailAddress.Should().BeNull(); result.Value.Profile.Timezone.Should().Be("atimezone"); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, "anid", "aname", OrganizationOwnership.Personal, - It.IsAny())); _userProfilesService.Verify(ups => - ups.CreateMachineProfilePrivateAsync(_caller.Object, "anid", "aname", Timezones.Default.ToString(), - CountryCodes.Default.ToString(), - It.IsAny())); + ups.GetProfilePrivateAsync(It.Is(cc => + cc.CallId == "acallid" + && cc.IsServiceAccount + ), "anid", It.IsAny())); } [Fact] @@ -571,14 +593,17 @@ public async Task WhenRegisterMachineAsyncByAuthenticatedUser_ThenRegistersWithB { _caller.Setup(cc => cc.IsAuthenticated) .Returns(true); + _caller.Setup(cc => cc.CallId) + .Returns("acallid"); var adder = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; _endUserRepository.Setup(rep => rep.LoadAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(adder); - adder.Register(Roles.Empty, Features.Empty, EmailAddress.Create("auser@company.com").Value); + adder.Register(Roles.Empty, Features.Empty, EndUserProfile.Create("afirstname").Value, + EmailAddress.Create("auser@company.com").Value); adder.AddMembership("anotherorganizationid".ToId(), Roles.Empty, Features.Empty); _userProfilesService.Setup(ups => - ups.CreateMachineProfilePrivateAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) + ups.GetProfilePrivateAsync(It.IsAny(), It.IsAny(), + It.IsAny())) .ReturnsAsync(new UserProfile { Id = "aprofileid", @@ -607,20 +632,18 @@ public async Task WhenRegisterMachineAsyncByAuthenticatedUser_ThenRegistersWithB result.Value.Roles.Should().ContainSingle(role => role == PlatformRoles.Standard.Name); result.Value.Features.Should().ContainSingle(feat => feat == PlatformFeatures.PaidTrial.Name); result.Value.Profile!.Id.Should().Be("aprofileid"); - result.Value.Profile.DefaultOrganizationId.Should().Be("anorganizationid"); + result.Value.Profile.DefaultOrganizationId.Should().Be("anotherorganizationid"); result.Value.Profile.Address.CountryCode.Should().Be("acountrycode"); result.Value.Profile.Name.FirstName.Should().Be("amachinename"); result.Value.Profile.Name.LastName.Should().BeNull(); result.Value.Profile.DisplayName.Should().Be("amachinename"); result.Value.Profile.EmailAddress.Should().BeNull(); result.Value.Profile.Timezone.Should().Be("atimezone"); - _organizationsService.Verify(os => - os.CreateOrganizationPrivateAsync(_caller.Object, "anid", "aname", OrganizationOwnership.Personal, - It.IsAny())); _userProfilesService.Verify(ups => - ups.CreateMachineProfilePrivateAsync(_caller.Object, "anid", "aname", Timezones.Default.ToString(), - CountryCodes.Default.ToString(), - It.IsAny())); + ups.GetProfilePrivateAsync(It.Is(cc => + cc.CallId == "acallid" + && cc.IsServiceAccount + ), "anid", It.IsAny())); } #if TESTINGONLY @@ -631,11 +654,12 @@ public async Task WhenAssignPlatformRolesAsync_ThenAssigns() .Returns("anassignerid"); var assignee = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; assignee.Register(Roles.Create(PlatformRoles.Standard).Value, Features.Create(PlatformFeatures.Basic).Value, - Optional.None); + EndUserProfile.Create("afirstname").Value, Optional.None); _endUserRepository.Setup(rep => rep.LoadAsync("anassigneeid".ToId(), It.IsAny())) .ReturnsAsync(assignee); var assigner = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; - assigner.Register(Roles.Create(PlatformRoles.Operations).Value, Features.Create(), Optional.None); + assigner.Register(Roles.Create(PlatformRoles.Operations).Value, Features.Create(), + EndUserProfile.Create("afirstname").Value, Optional.None); _endUserRepository.Setup(rep => rep.LoadAsync("anassignerid".ToId(), It.IsAny())) .ReturnsAsync(assigner); @@ -656,12 +680,13 @@ public async Task WhenUnassignPlatformRolesAsync_ThenUnassigns() .Returns("anassignerid"); var assignee = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; assignee.Register(Roles.Create(PlatformRoles.Standard, PlatformRoles.TestingOnly).Value, - Features.Create(PlatformFeatures.Basic).Value, + Features.Create(PlatformFeatures.Basic).Value, EndUserProfile.Create("afirstname").Value, Optional.None); _endUserRepository.Setup(rep => rep.LoadAsync("anassigneeid".ToId(), It.IsAny())) .ReturnsAsync(assignee); var assigner = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; - assigner.Register(Roles.Create(PlatformRoles.Operations).Value, Features.Create(), Optional.None); + assigner.Register(Roles.Create(PlatformRoles.Operations).Value, Features.Create(), + EndUserProfile.Create("afirstname").Value, Optional.None); _endUserRepository.Setup(rep => rep.LoadAsync("anassignerid".ToId(), It.IsAny())) .ReturnsAsync(assigner); @@ -682,13 +707,14 @@ public async Task WhenAssignTenantRolesAsync_ThenAssigns() .Returns("anassignerid"); var assignee = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; assignee.Register(Roles.Create(PlatformRoles.Standard).Value, Features.Create(PlatformFeatures.Basic).Value, - Optional.None); + EndUserProfile.Create("afirstname").Value, Optional.None); assignee.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Member).Value, Features.Create(TenantFeatures.Basic).Value); _endUserRepository.Setup(rep => rep.LoadAsync("anassigneeid".ToId(), It.IsAny())) .ReturnsAsync(assignee); var assigner = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; - assigner.Register(Roles.Create(PlatformRoles.Operations).Value, Features.Create(), Optional.None); + assigner.Register(Roles.Create(PlatformRoles.Operations).Value, Features.Create(), + EndUserProfile.Create("afirstname").Value, Optional.None); assigner.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Owner).Value, Features.Create(TenantFeatures.Basic).Value); _endUserRepository.Setup(rep => rep.LoadAsync("anassignerid".ToId(), It.IsAny())) @@ -768,7 +794,7 @@ public async Task WhenGetMembershipsAsync_ThenReturnsUser() { var user = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; user.Register(Roles.Create(PlatformRoles.Standard).Value, Features.Create(PlatformFeatures.Basic).Value, - EmailAddress.Create("auser@company.com").Value); + EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); user.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Member).Value, Features.Create(TenantFeatures.PaidTrial).Value); _endUserRepository.Setup(rep => rep.LoadAsync(It.IsAny(), It.IsAny())) @@ -789,37 +815,4 @@ public async Task WhenGetMembershipsAsync_ThenReturnsUser() result.Value.Memberships[0].Roles.Should().ContainSingle(role => role == TenantRoles.Member.Name); result.Value.Memberships[0].Features.Should().ContainSingle(feat => feat == TenantFeatures.PaidTrial.Name); } - - [Fact] - public async Task WhenCreateMembershipForCallerAsyncAndUserNoExist_ThenReturnsError() - { - _endUserRepository.Setup(rep => rep.LoadAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(Error.EntityNotFound()); - - var result = - await _application.CreateMembershipForCallerAsync(_caller.Object, "anorganizationid", - CancellationToken.None); - - result.Should().BeError(ErrorCode.EntityNotFound); - } - - [Fact] - public async Task WhenCreateMembershipForCallerAsync_ThenAddsMembership() - { - var user = EndUserRoot.Create(_recorder.Object, _idFactory.Object, UserClassification.Person).Value; - user.Register(Roles.Create(PlatformRoles.Standard).Value, Features.Create(PlatformFeatures.Basic).Value, - EmailAddress.Create("auser@company.com").Value); - _endUserRepository.Setup(rep => rep.LoadAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(user); - - var result = - await _application.CreateMembershipForCallerAsync(_caller.Object, "anorganizationid", - CancellationToken.None); - - result.Should().BeSuccess(); - result.Value.IsDefault.Should().BeTrue(); - result.Value.OrganizationId.Should().Be("anorganizationid"); - result.Value.Roles.Should().ContainSingle(role => role == TenantRoles.Member.Name); - result.Value.Features.Should().ContainSingle(feat => feat == TenantFeatures.Basic.Name); - } } \ No newline at end of file diff --git a/src/EndUsersApplication.UnitTests/InvitationsApplicationSpec.cs b/src/EndUsersApplication.UnitTests/InvitationsApplicationSpec.cs index 86cfcaa3..1166b02f 100644 --- a/src/EndUsersApplication.UnitTests/InvitationsApplicationSpec.cs +++ b/src/EndUsersApplication.UnitTests/InvitationsApplicationSpec.cs @@ -8,6 +8,7 @@ using Domain.Interfaces.Entities; using Domain.Services.Shared.DomainServices; using Domain.Shared; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence; using EndUsersDomain; using FluentAssertions; @@ -65,7 +66,8 @@ public async Task WhenInviteGuestAsyncAndInviteeAlreadyRegistered_ThenReturnsErr .ReturnsAsync(inviter); var invitee = EndUserRoot .Create(_recorder.Object, "aninviteeid".ToIdentifierFactory(), UserClassification.Person).Value; - invitee.Register(Roles.Empty, Features.Empty, EmailAddress.Create("aninvitee@company.com").Value); + invitee.Register(Roles.Empty, Features.Empty, EndUserProfile.Create("afirstname").Value, + EmailAddress.Create("aninvitee@company.com").Value); _invitationRepository.Setup(rep => rep.FindInvitedGuestByEmailAddressAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(invitee.ToOptional()); diff --git a/src/EndUsersApplication/EndUsersApplication.DomainEventHandlers.cs b/src/EndUsersApplication/EndUsersApplication.DomainEventHandlers.cs new file mode 100644 index 00000000..843df232 --- /dev/null +++ b/src/EndUsersApplication/EndUsersApplication.DomainEventHandlers.cs @@ -0,0 +1,25 @@ +using Application.Interfaces; +using Application.Resources.Shared; +using Common; +using Common.Extensions; +using Domain.Common.ValueObjects; +using Domain.Events.Shared.Organizations; + +namespace EndUsersApplication; + +partial class EndUsersApplication +{ + public async Task> HandleOrganizationCreatedAsync(ICallerContext caller, Created domainEvent, + CancellationToken cancellationToken) + { + var ownership = domainEvent.Ownership.ToEnumOrDefault(OrganizationOwnership.Shared); + var membership = await CreateMembershipAsync(caller, domainEvent.CreatedById.ToId(), domainEvent.RootId.ToId(), + ownership, cancellationToken); + if (!membership.IsSuccessful) + { + return membership.Error; + } + + return Result.Ok; + } +} \ No newline at end of file diff --git a/src/EndUsersApplication/EndUsersApplication.cs b/src/EndUsersApplication/EndUsersApplication.cs index 10d3565f..df72e6ef 100644 --- a/src/EndUsersApplication/EndUsersApplication.cs +++ b/src/EndUsersApplication/EndUsersApplication.cs @@ -1,3 +1,4 @@ +using Application.Common; using Application.Common.Extensions; using Application.Interfaces; using Application.Resources.Shared; @@ -8,16 +9,17 @@ using Domain.Common.Identity; using Domain.Common.ValueObjects; using Domain.Shared; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence; using EndUsersApplication.Persistence.ReadModels; using EndUsersDomain; using EndUser = Application.Resources.Shared.EndUser; using Membership = Application.Resources.Shared.Membership; -using PersonName = Domain.Shared.PersonName; +using PersonName = Application.Resources.Shared.PersonName; namespace EndUsersApplication; -public class EndUsersApplication : IEndUsersApplication +public partial class EndUsersApplication : IEndUsersApplication { internal const string PermittedOperatorsSettingName = "Hosts:EndUsersApi:Authorization:OperatorWhitelist"; private static readonly char[] PermittedOperatorsDelimiters = [';', ',', ' ']; @@ -25,21 +27,19 @@ public class EndUsersApplication : IEndUsersApplication private readonly IIdentifierFactory _idFactory; private readonly IInvitationRepository _invitationRepository; private readonly INotificationsService _notificationsService; - private readonly IOrganizationsService _organizationsService; private readonly IRecorder _recorder; private readonly IConfigurationSettings _settings; private readonly IUserProfilesService _userProfilesService; public EndUsersApplication(IRecorder recorder, IIdentifierFactory idFactory, IConfigurationSettings settings, - INotificationsService notificationsService, IOrganizationsService organizationsService, - IUserProfilesService userProfilesService, IInvitationRepository invitationRepository, + INotificationsService notificationsService, IUserProfilesService userProfilesService, + IInvitationRepository invitationRepository, IEndUserRepository endUserRepository) { _recorder = recorder; _idFactory = idFactory; _settings = settings; _notificationsService = notificationsService; - _organizationsService = organizationsService; _userProfilesService = userProfilesService; _invitationRepository = invitationRepository; _endUserRepository = endUserRepository; @@ -108,38 +108,31 @@ public async Task> RegisterMachineAsync(ICaller return created.Error; } - var machine = created.Value; - var profiled = await _userProfilesService.CreateMachineProfilePrivateAsync(context, machine.Id, name, timezone, - countryCode, cancellationToken); - if (!profiled.IsSuccessful) + var userProfile = EndUserProfile.Create(name, timezone: timezone, countryCode: countryCode); + if (!userProfile.IsSuccessful) { - return profiled.Error; + return userProfile.Error; } - var profile = profiled.Value; - var (platformRoles, platformFeatures, tenantRoles, tenantFeatures) = + var machine = created.Value; + var (platformRoles, platformFeatures, _, _) = EndUserRoot.GetInitialRolesAndFeatures(RolesAndFeaturesUseCase.CreatingMachine, context.IsAuthenticated); - var registered = machine.Register(platformRoles, platformFeatures, Optional.None); + var registered = + machine.Register(platformRoles, platformFeatures, userProfile.Value, Optional.None); if (!registered.IsSuccessful) { return registered.Error; } - var defaultOrganization = - await _organizationsService.CreateOrganizationPrivateAsync(context, machine.Id, name, - OrganizationOwnership.Personal, cancellationToken); - if (!defaultOrganization.IsSuccessful) + var saved = await _endUserRepository.SaveAsync(machine, true, cancellationToken); + if (!saved.IsSuccessful) { - return defaultOrganization.Error; + return saved.Error; } - var defaultOrganizationId = defaultOrganization.Value.Id.ToId(); - var selfEnrolled = machine.AddMembership(defaultOrganizationId, tenantRoles, - tenantFeatures); - if (!selfEnrolled.IsSuccessful) - { - return selfEnrolled.Error; - } + machine = saved.Value; + _recorder.TraceInformation(context.ToCall(), "Registered machine: {Id}", machine.Id); + _recorder.TrackUsage(context.ToCall(), UsageConstants.Events.UsageScenarios.MachineRegistered); if (context.IsAuthenticated) { @@ -152,25 +145,35 @@ await _organizationsService.CreateOrganizationPrivateAsync(context, machine.Id, var (_, _, tenantRoles2, tenantFeatures2) = EndUserRoot.GetInitialRolesAndFeatures(RolesAndFeaturesUseCase.InvitingMachineToCreatorOrg, context.IsAuthenticated); - var adderDefaultOrganizationId = adder.Value.Memberships.DefaultMembership.OrganizationId; + var adderDefaultOrganizationId = adder.Value.DefaultMembership.OrganizationId; var adderEnrolled = machine.AddMembership(adderDefaultOrganizationId, tenantRoles2, tenantFeatures2); if (!adderEnrolled.IsSuccessful) { return adderEnrolled.Error; } + + saved = await _endUserRepository.SaveAsync(saved.Value, cancellationToken); + if (!saved.IsSuccessful) + { + return saved.Error; + } + + machine = saved.Value; + _recorder.TraceInformation(context.ToCall(), + "Machine {Id} has become a member of {User} organization {Organization}", + machine.Id, adder.Value.Id, adderDefaultOrganizationId); } - var saved = await _endUserRepository.SaveAsync(machine, cancellationToken); - if (!saved.IsSuccessful) + var defaultOrganizationId = machine.DefaultMembership.OrganizationId; + var serviceCaller = Caller.CreateAsMaintenance(context.CallId); + var profile = await _userProfilesService.GetProfilePrivateAsync(serviceCaller, machine.Id, cancellationToken); + if (!profile.IsSuccessful) { - return saved.Error; + return profile.Error; } - _recorder.TraceInformation(context.ToCall(), "Registered machine: {Id}", machine.Id); - _recorder.TrackUsage(context.ToCall(), UsageConstants.Events.UsageScenarios.MachineRegistered); - - return machine.ToRegisteredUser(defaultOrganizationId, profile); + return machine.ToRegisteredUser(defaultOrganizationId, profile.Value); } public async Task> RegisterPersonAsync(ICallerContext context, @@ -240,24 +243,24 @@ public async Task> RegisterPersonAsync(ICallerC } EndUserRoot unregisteredUser; - UserProfile? profile; if (existingUser.HasValue) { unregisteredUser = existingUser.Value.User; if (unregisteredUser.Status == UserStatus.Registered) { - profile = existingUser.Value.Profile; - if (profile.NotExists() - || profile.Classification != UserProfileClassification.Person - || profile.EmailAddress.HasNoValue()) + var unregisteredUserProfile = existingUser.Value.Profile; + if (unregisteredUserProfile.NotExists() + || unregisteredUserProfile.Classification != UserProfileClassification.Person + || unregisteredUserProfile.EmailAddress.HasNoValue()) { return Error.EntityNotFound(Resources.EndUsersApplication_NotPersonProfile); } var notified = await _notificationsService.NotifyReRegistrationCourtesyAsync(context, unregisteredUser.Id, - profile.EmailAddress, profile.DisplayName, profile.Timezone, profile.Address.CountryCode, + unregisteredUserProfile.EmailAddress, unregisteredUserProfile.DisplayName, + unregisteredUserProfile.Timezone, unregisteredUserProfile.Address.CountryCode, cancellationToken); if (!notified.IsSuccessful) { @@ -273,7 +276,8 @@ public async Task> RegisterPersonAsync(ICallerC { UsageConstants.Properties.EmailAddress, email } }); - return unregisteredUser.ToRegisteredUser(unregisteredUser.Memberships.DefaultMembership.Id, profile); + return unregisteredUser.ToRegisteredUser(unregisteredUser.DefaultMembership.Id, + unregisteredUserProfile); } } else @@ -287,97 +291,44 @@ public async Task> RegisterPersonAsync(ICallerC unregisteredUser = created.Value; } - var profiled = await _userProfilesService.CreatePersonProfilePrivateAsync(context, unregisteredUser.Id, - username, firstName, lastName, timezone, countryCode, cancellationToken); - if (!profiled.IsSuccessful) + var userProfile = EndUserProfile.Create(firstName, lastName, timezone, countryCode); + if (!userProfile.IsSuccessful) { - return profiled.Error; + return userProfile.Error; } - profile = profiled.Value; var permittedOperators = GetPermittedOperators(); - var (platformRoles, platformFeatures, tenantRoles, tenantFeatures) = + var (platformRoles, platformFeatures, _, _) = EndUserRoot.GetInitialRolesAndFeatures(RolesAndFeaturesUseCase.CreatingPerson, context.IsAuthenticated, - username, - permittedOperators); - var registered = unregisteredUser.Register(platformRoles, platformFeatures, username); + username, permittedOperators); + var registered = unregisteredUser.Register(platformRoles, platformFeatures, userProfile.Value, username); if (!registered.IsSuccessful) { return registered.Error; } - var organizationName = PersonName.Create(firstName, lastName); - if (!organizationName.IsSuccessful) - { - return organizationName.Error; - } - - var defaultOrganization = - await _organizationsService.CreateOrganizationPrivateAsync(context, unregisteredUser.Id, - organizationName.Value.FullName, OrganizationOwnership.Personal, - cancellationToken); - if (!defaultOrganization.IsSuccessful) - { - return defaultOrganization.Error; - } - - var defaultOrganizationId = defaultOrganization.Value.Id.ToId(); - var enrolled = unregisteredUser.AddMembership(defaultOrganizationId, tenantRoles, - tenantFeatures); - if (!enrolled.IsSuccessful) - { - return enrolled.Error; - } - - var saved = await _endUserRepository.SaveAsync(unregisteredUser, cancellationToken); + var saved = await _endUserRepository.SaveAsync(unregisteredUser, true, cancellationToken); if (!saved.IsSuccessful) { return saved.Error; } - _recorder.TraceInformation(context.ToCall(), "Registered user: {Id}", unregisteredUser.Id); - _recorder.AuditAgainst(context.ToCall(), unregisteredUser.Id, + var person = saved.Value; + _recorder.TraceInformation(context.ToCall(), "Registered user: {Id}", person.Id); + _recorder.AuditAgainst(context.ToCall(), person.Id, Audits.EndUsersApplication_User_Registered_TermsAccepted, - "EndUser {Id} accepted their terms and conditions", unregisteredUser.Id); + "EndUser {Id} accepted their terms and conditions", person.Id); _recorder.TrackUsage(context.ToCall(), UsageConstants.Events.UsageScenarios.PersonRegistrationCreated); - return unregisteredUser.ToRegisteredUser(defaultOrganizationId, profile); - } - - public async Task> CreateMembershipForCallerAsync(ICallerContext context, - string organizationId, CancellationToken cancellationToken) - { - var retrieved = await _endUserRepository.LoadAsync(context.ToCallerId(), cancellationToken); - if (!retrieved.IsSuccessful) - { - return retrieved.Error; - } - - var user = retrieved.Value; - var (_, _, tenantRoles, tenantFeatures) = - EndUserRoot.GetInitialRolesAndFeatures(RolesAndFeaturesUseCase.CreatingOrg, context.IsAuthenticated); - var membered = user.AddMembership(organizationId.ToId(), tenantRoles, tenantFeatures); - if (!membered.IsSuccessful) - { - return membered.Error; - } - - var saved = await _endUserRepository.SaveAsync(user, cancellationToken); - if (!saved.IsSuccessful) + var defaultOrganizationId = person.DefaultMembership.OrganizationId; + var serviceCaller = Caller.CreateAsMaintenance(context.CallId); + var profile = await _userProfilesService.GetProfilePrivateAsync(serviceCaller, person.Id, cancellationToken); + if (!profile.IsSuccessful) { - return saved.Error; + return profile.Error; } - _recorder.TraceInformation(context.ToCall(), "EndUser {Id} has become a member of organization {Organization}", - user.Id, organizationId); - - var membership = saved.Value.FindMembership(organizationId.ToId()); - if (!membership.HasValue) - { - return Error.EntityNotFound(Resources.EndUsersApplication_MembershipNotFound); - } - - return membership.Value.ToMembership(); + return person.ToRegisteredUser(defaultOrganizationId, profile.Value); } public async Task, Error>> FindPersonByEmailAddressAsync(ICallerContext context, @@ -541,6 +492,51 @@ public async Task> AssignTenantRolesAsync( return assignee.ToUserWithMemberships(); } + private async Task> CreateMembershipAsync(ICallerContext context, + Identifier createdById, Identifier organizationId, OrganizationOwnership ownership, + CancellationToken cancellationToken) + { + var retrieved = await _endUserRepository.LoadAsync(createdById, cancellationToken); + if (!retrieved.IsSuccessful) + { + return retrieved.Error; + } + + var creator = retrieved.Value; + var useCase = ownership switch + { + OrganizationOwnership.Shared => RolesAndFeaturesUseCase.CreatingOrg, + OrganizationOwnership.Personal => creator.Classification == UserClassification.Person + ? RolesAndFeaturesUseCase.CreatingPerson + : RolesAndFeaturesUseCase.CreatingMachine, + _ => RolesAndFeaturesUseCase.CreatingOrg + }; + var (_, _, tenantRoles, tenantFeatures) = + EndUserRoot.GetInitialRolesAndFeatures(useCase, context.IsAuthenticated); + var membered = creator.AddMembership(organizationId, tenantRoles, tenantFeatures); + if (!membered.IsSuccessful) + { + return membered.Error; + } + + var saved = await _endUserRepository.SaveAsync(creator, cancellationToken); + if (!saved.IsSuccessful) + { + return saved.Error; + } + + _recorder.TraceInformation(context.ToCall(), "EndUser {Id} has become a member of organization {Organization}", + creator.Id, organizationId); + + var membership = saved.Value.FindMembership(organizationId); + if (!membership.HasValue) + { + return Error.EntityNotFound(Resources.EndUsersApplication_MembershipNotFound); + } + + return membership.Value.ToMembership(); + } + private async Task> WithGetOptionsAsync(ICallerContext caller, List memberships, GetOptions options, CancellationToken cancellationToken) { @@ -732,7 +728,7 @@ public static UserProfile ToUnregisteredUserProfile(this MembershipJoinInvitatio UserId = membership.UserId.Value, EmailAddress = membership.InvitedEmailAddress.Value, DisplayName = membership.InvitedEmailAddress.Value, - Name = new Application.Resources.Shared.PersonName + Name = new PersonName { FirstName = membership.InvitedEmailAddress.Value, LastName = null diff --git a/src/EndUsersApplication/IEndUsersApplication.DomainEventHandlers.cs b/src/EndUsersApplication/IEndUsersApplication.DomainEventHandlers.cs new file mode 100644 index 00000000..e1c80b1c --- /dev/null +++ b/src/EndUsersApplication/IEndUsersApplication.DomainEventHandlers.cs @@ -0,0 +1,11 @@ +using Application.Interfaces; +using Common; +using Domain.Events.Shared.Organizations; + +namespace EndUsersApplication; + +partial interface IEndUsersApplication +{ + Task> HandleOrganizationCreatedAsync(ICallerContext caller, Created domainEvent, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/EndUsersApplication/IEndUsersApplication.cs b/src/EndUsersApplication/IEndUsersApplication.cs index 7daf27d8..a31956ff 100644 --- a/src/EndUsersApplication/IEndUsersApplication.cs +++ b/src/EndUsersApplication/IEndUsersApplication.cs @@ -4,7 +4,7 @@ namespace EndUsersApplication; -public interface IEndUsersApplication +public partial interface IEndUsersApplication { Task> AssignPlatformRolesAsync(ICallerContext context, string id, List roles, CancellationToken cancellationToken); @@ -13,9 +13,6 @@ Task> AssignTenantRolesAsync(ICallerContex string id, List roles, CancellationToken cancellationToken); - Task> CreateMembershipForCallerAsync(ICallerContext context, string organizationId, - CancellationToken cancellationToken); - Task, Error>> FindPersonByEmailAddressAsync(ICallerContext context, string emailAddress, CancellationToken cancellationToken); diff --git a/src/EndUsersApplication/InvitationsApplication.cs b/src/EndUsersApplication/InvitationsApplication.cs index 832908cc..7f419916 100644 --- a/src/EndUsersApplication/InvitationsApplication.cs +++ b/src/EndUsersApplication/InvitationsApplication.cs @@ -8,6 +8,7 @@ using Domain.Common.ValueObjects; using Domain.Services.Shared.DomainServices; using Domain.Shared; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence; using EndUsersDomain; using Membership = Application.Resources.Shared.Membership; diff --git a/src/EndUsersApplication/Persistence/IEndUserRepository.cs b/src/EndUsersApplication/Persistence/IEndUserRepository.cs index cd08c43b..cf3c2854 100644 --- a/src/EndUsersApplication/Persistence/IEndUserRepository.cs +++ b/src/EndUsersApplication/Persistence/IEndUserRepository.cs @@ -13,6 +13,8 @@ public interface IEndUserRepository : IApplicationRepository Task> SaveAsync(EndUserRoot user, CancellationToken cancellationToken); + Task> SaveAsync(EndUserRoot user, bool reload, CancellationToken cancellationToken); + Task, Error>> SearchAllMembershipsByOrganizationAsync( Identifier organizationId, SearchOptions searchOptions, CancellationToken cancellationToken); diff --git a/src/EndUsersApplication/Persistence/ReadModels/EndUser.cs b/src/EndUsersApplication/Persistence/ReadModels/EndUser.cs index cbc87d27..c8aea3fb 100644 --- a/src/EndUsersApplication/Persistence/ReadModels/EndUser.cs +++ b/src/EndUsersApplication/Persistence/ReadModels/EndUser.cs @@ -1,6 +1,7 @@ using Application.Persistence.Common; using Common; using Domain.Shared; +using Domain.Shared.EndUsers; using QueryAny; namespace EndUsersApplication.Persistence.ReadModels; @@ -8,15 +9,15 @@ namespace EndUsersApplication.Persistence.ReadModels; [EntityName("EndUser")] public class EndUser : ReadModelEntity { - public Optional Access { get; set; } + public Optional Access { get; set; } - public Optional Classification { get; set; } + public Optional Classification { get; set; } public Optional Features { get; set; } public Optional Roles { get; set; } - public Optional Status { get; set; } + public Optional Status { get; set; } public Optional Username { get; set; } } \ No newline at end of file diff --git a/src/EndUsersApplication/Persistence/ReadModels/Invitation.cs b/src/EndUsersApplication/Persistence/ReadModels/Invitation.cs index ce7c64c3..02b560de 100644 --- a/src/EndUsersApplication/Persistence/ReadModels/Invitation.cs +++ b/src/EndUsersApplication/Persistence/ReadModels/Invitation.cs @@ -1,5 +1,6 @@ using Application.Persistence.Common; using Common; +using Domain.Shared.EndUsers; using QueryAny; namespace EndUsersApplication.Persistence.ReadModels; @@ -15,7 +16,7 @@ public class Invitation : ReadModelEntity public Optional InvitedEmailAddress { get; set; } - public Optional Status { get; set; } + public Optional Status { get; set; } public Optional Token { get; set; } } \ No newline at end of file diff --git a/src/EndUsersApplication/Persistence/ReadModels/MembershipJoinInvitation.cs b/src/EndUsersApplication/Persistence/ReadModels/MembershipJoinInvitation.cs index 8440e1ca..997ed02a 100644 --- a/src/EndUsersApplication/Persistence/ReadModels/MembershipJoinInvitation.cs +++ b/src/EndUsersApplication/Persistence/ReadModels/MembershipJoinInvitation.cs @@ -1,6 +1,7 @@ using Application.Persistence.Common; using Common; using Domain.Shared; +using Domain.Shared.EndUsers; using QueryAny; namespace EndUsersApplication.Persistence.ReadModels; @@ -8,17 +9,17 @@ namespace EndUsersApplication.Persistence.ReadModels; [EntityName("Membership")] public class MembershipJoinInvitation : ReadModelEntity { - public Optional UserId { get; set; } + public Optional Features { get; set; } - public Optional OrganizationId { get; set; } + public Optional InvitedEmailAddress { get; set; } public bool IsDefault { get; set; } - public Optional Roles { get; set; } + public Optional OrganizationId { get; set; } - public Optional Features { get; set; } + public Optional Roles { get; set; } - public Optional InvitedEmailAddress { get; set; } + public Optional Status { get; set; } - public Optional Status { get; set; } + public Optional UserId { get; set; } } \ No newline at end of file diff --git a/src/EndUsersDomain.UnitTests/EndUserRootSpec.cs b/src/EndUsersDomain.UnitTests/EndUserRootSpec.cs index a81b0f57..7bb9f841 100644 --- a/src/EndUsersDomain.UnitTests/EndUserRootSpec.cs +++ b/src/EndUsersDomain.UnitTests/EndUserRootSpec.cs @@ -8,6 +8,7 @@ using Domain.Interfaces.Entities; using Domain.Services.Shared.DomainServices; using Domain.Shared; +using Domain.Shared.EndUsers; using FluentAssertions; using Moq; using UnitTesting.Common; @@ -60,10 +61,11 @@ public void WhenConstructed_ThenAssigned() public async Task WhenRegisterAndInvitedAsGuest_ThenAcceptsInvitationAndRegistered() { var emailAddress = EmailAddress.Create("auser@company.com").Value; + var userProfile = EndUserProfile.Create("afirstname").Value; await _user.InviteGuestAsync(_tokensService.Object, "aninviterid".ToId(), emailAddress, (_, _) => Task.FromResult(Result.Ok)); _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, emailAddress); + Features.Create(PlatformFeatures.Basic.Name).Value, userProfile, emailAddress); _user.Access.Should().Be(UserAccess.Enabled); _user.Status.Should().Be(UserStatus.Registered); @@ -80,7 +82,7 @@ await _user.InviteGuestAsync(_tokensService.Object, "aninviterid".ToId(), emailA public void WhenRegister_ThenRegistered() { _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); _user.Access.Should().Be(UserAccess.Enabled); @@ -104,9 +106,8 @@ public void WhenEnsureInvariantsAndMachineIsNotRegistered_ThenReturnsError() [Fact] public void WhenEnsureInvariantsAndRegisteredPersonDoesNotHaveADefaultRole_ThenReturnsError() { - _user.Register(Roles.Create(), - Features.Create(PlatformFeatures.Basic.Name).Value, - EmailAddress.Create("auser@company.com").Value); + _user.Register(Roles.Create(), Features.Create(PlatformFeatures.Basic.Name).Value, + EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); var result = _user.EnsureInvariants(); @@ -117,7 +118,7 @@ public void WhenEnsureInvariantsAndRegisteredPersonDoesNotHaveADefaultRole_ThenR public void WhenEnsureInvariantsAndRegisteredPersonDoesNotHaveADefaultFeature_ThenReturnsError() { _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(), + Features.Create(), EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); var result = _user.EnsureInvariants(); @@ -130,7 +131,7 @@ public void WhenEnsureInvariantsAndRegisteredPersonStillInvited_ThenReturnsError { var emailAddress = EmailAddress.Create("auser@company.com").Value; _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, emailAddress); #if TESTINGONLY _user.TestingOnly_InviteGuest(emailAddress); @@ -145,7 +146,7 @@ public void WhenEnsureInvariantsAndRegisteredPersonStillInvited_ThenReturnsError public void WhenAddMembershipAndAlreadyMember_ThenReturns() { _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); _user.AddMembership("anorganizationid".ToId(), Roles.Create(), Features.Create()); @@ -158,7 +159,7 @@ public void WhenAddMembershipAndAlreadyMember_ThenReturns() public void WhenAddMembership_ThenAddsMembershipAsDefaultWithRolesAndFeatures() { _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); var roles = Roles.Create(TenantRoles.Member).Value; var features = Features.Create(TenantFeatures.Basic).Value; @@ -178,7 +179,7 @@ public void WhenAddMembership_ThenAddsMembershipAsDefaultWithRolesAndFeatures() public void WhenAddMembershipAndHasMembership_ThenChangesNextToDefaultMembership() { _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); var roles = Roles.Create(TenantRoles.Member).Value; var features = Features.Create(TenantFeatures.Basic).Value; @@ -231,7 +232,7 @@ public void WhenAssignMembershipFeaturesAndFeatureNotAssignable_ThenReturnsError { var assigner = CreateOrgOwner("anorganizationid"); _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); _user.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Member).Value, Features.Create(TenantFeatures.Basic).Value); @@ -249,7 +250,7 @@ public void WhenAssignMembershipFeatures_ThenAssigns() { var assigner = CreateOrgOwner("anorganizationid"); _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); _user.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Member).Value, Features.Create(TenantFeatures.Basic).Value); @@ -283,7 +284,7 @@ public void WhenAssignMembershipRolesAndRoleNotAssignable_ThenReturnsError() { var assigner = CreateOrgOwner("anorganizationid"); _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); _user.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Member).Value, Features.Create(TenantFeatures.Basic).Value); @@ -301,7 +302,7 @@ public void WhenAssignMembershipRoles_ThenAssigns() { var assigner = CreateOrgOwner("anorganizationid"); _user.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("auser@company.com").Value); _user.AddMembership("anorganizationid".ToId(), Roles.Create(TenantRoles.Member).Value, Features.Create(TenantFeatures.Basic).Value); @@ -463,7 +464,7 @@ public void WhenUnassignPlatformRoles_ThenUnassigns() public async Task WhenInviteAsGuestAndRegistered_ThenDoesNothing() { var emailAddress = EmailAddress.Create("invitee@company.com").Value; - _user.Register(Roles.Empty, Features.Empty, emailAddress); + _user.Register(Roles.Empty, Features.Empty, EndUserProfile.Create("afirstname").Value, emailAddress); var wasCallbackCalled = false; await _user.InviteGuestAsync(_tokensService.Object, "aninviterid".ToId(), emailAddress, @@ -582,7 +583,7 @@ await _user.ReInviteGuestAsync(_tokensService.Object, "aninviterid".ToId(), public void WhenVerifyGuestInvitationAndAlreadyRegistered_ThenReturnsError() { var emailAddress = EmailAddress.Create("invitee@company.com").Value; - _user.Register(Roles.Empty, Features.Empty, emailAddress); + _user.Register(Roles.Empty, Features.Empty, EndUserProfile.Create("afirstname").Value, emailAddress); var result = _user.VerifyGuestInvitation(); @@ -639,7 +640,7 @@ public void WhenAcceptGuestInvitationAndAuthenticatedUser_ThenReturnsError() public void WhenAcceptGuestInvitationAndRegistered_ThenReturnsError() { var emailAddress = EmailAddress.Create("auser@company.com").Value; - _user.Register(Roles.Empty, Features.Empty, emailAddress); + _user.Register(Roles.Empty, Features.Empty, EndUserProfile.Create("afirstname").Value, emailAddress); var result = _user.AcceptGuestInvitation(CallerConstants.AnonymousUserId.ToId(), emailAddress); @@ -690,7 +691,7 @@ private EndUserRoot CreateOrgOwner(string organizationId) { var owner = EndUserRoot.Create(_recorder.Object, _identifierFactory.Object, UserClassification.Person).Value; owner.Register(Roles.Create(PlatformRoles.Standard.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("orgowner@company.com").Value); owner.AddMembership(organizationId.ToId(), Roles.Create(TenantRoles.Owner).Value, Features.Empty); @@ -702,7 +703,7 @@ private EndUserRoot CreateOperator() var @operator = EndUserRoot.Create(_recorder.Object, _identifierFactory.Object, UserClassification.Person) .Value; @operator.Register(Roles.Create(PlatformRoles.Standard.Name, PlatformRoles.Operations.Name).Value, - Features.Create(PlatformFeatures.Basic.Name).Value, + Features.Create(PlatformFeatures.Basic.Name).Value, EndUserProfile.Create("afirstname").Value, EmailAddress.Create("operator@company.com").Value); return @operator; diff --git a/src/EndUsersDomain/EndUserProfile.cs b/src/EndUsersDomain/EndUserProfile.cs new file mode 100644 index 00000000..62a9b9e9 --- /dev/null +++ b/src/EndUsersDomain/EndUserProfile.cs @@ -0,0 +1,63 @@ +using Common; +using Domain.Common.ValueObjects; +using Domain.Interfaces; +using Domain.Shared; + +namespace EndUsersDomain; + +public sealed class EndUserProfile : ValueObjectBase +{ + public static Result Create(string firstName, string? lastName = null, + string? timezone = null, + string? countryCode = null) + { + var name = PersonName.Create(firstName, lastName); + if (!name.IsSuccessful) + { + return name.Error; + } + + var tz = Timezone.Create(Timezones.FindOrDefault(timezone)); + if (!tz.IsSuccessful) + { + return tz.Error; + } + + var address = Address.Create(CountryCodes.FindOrDefault(countryCode)); + if (!address.IsSuccessful) + { + return address.Error; + } + + return new EndUserProfile(name.Value, tz.Value, address.Value); + } + + private EndUserProfile(PersonName name, Timezone timezone, Address address) + { + Name = name; + Timezone = timezone; + Address = address; + } + + public Address Address { get; } + + public PersonName Name { get; } + + public Timezone Timezone { get; } + + public static ValueObjectFactory Rehydrate() + { + return (property, container) => + { + var parts = RehydrateToList(property, false); + return new EndUserProfile(PersonName.Rehydrate()(parts[0]!, container), + Timezone.Rehydrate()(parts[1]!, container), + Address.Rehydrate()(parts[2]!, container)); + }; + } + + protected override IEnumerable GetAtomicValues() + { + return new object[] { Name, Timezone, Address }; + } +} \ No newline at end of file diff --git a/src/EndUsersDomain/EndUserRoot.cs b/src/EndUsersDomain/EndUserRoot.cs index 5df9aa3e..40f6ff23 100644 --- a/src/EndUsersDomain/EndUserRoot.cs +++ b/src/EndUsersDomain/EndUserRoot.cs @@ -10,6 +10,7 @@ using Domain.Interfaces.ValueObjects; using Domain.Services.Shared.DomainServices; using Domain.Shared; +using Domain.Shared.EndUsers; namespace EndUsersDomain; @@ -38,6 +39,8 @@ private EndUserRoot(IRecorder recorder, IIdentifierFactory idFactory, ISingleVal public UserClassification Classification { get; private set; } + public Membership DefaultMembership => Memberships.DefaultMembership; + public Features Features { get; private set; } = Features.Create(); public GuestInvitation GuestInvitation { get; private set; } = GuestInvitation.Empty; @@ -106,9 +109,9 @@ protected override Result OnStateChanged(IDomainEvent @event, bool isReco { case Created created: { - Access = created.Access.ToEnumOrDefault(UserAccess.Enabled); - Status = created.Status.ToEnumOrDefault(UserStatus.Unregistered); - Classification = created.Classification.ToEnumOrDefault(UserClassification.Person); + Access = created.Access; + Status = created.Status; + Classification = created.Classification; Features = Features.Create(); Roles = Roles.Create(); return Result.Ok; @@ -116,9 +119,9 @@ protected override Result OnStateChanged(IDomainEvent @event, bool isReco case Registered changed: { - Access = changed.Access.ToEnumOrDefault(UserAccess.Enabled); - Status = changed.Status.ToEnumOrDefault(UserStatus.Unregistered); - Classification = changed.Classification.ToEnumOrDefault(UserClassification.Person); + Access = changed.Access; + Status = changed.Status; + Classification = changed.Classification; var roles = Roles.Create(changed.Roles.ToArray()); if (!roles.IsSuccessful) @@ -576,7 +579,7 @@ public async Task> InviteGuestAsync(ITokensService tokensService, return await onInvited(inviterId, token); } - public Result Register(Roles roles, Features levels, Optional username) + public Result Register(Roles roles, Features levels, EndUserProfile profile, Optional username) { if (Status != UserStatus.Unregistered) { @@ -595,7 +598,7 @@ public Result Register(Roles roles, Features levels, Optional username, - UserClassification classification, - UserAccess access, UserStatus status, - Roles roles, - Features features) + public static Registered Registered(Identifier id, EndUserProfile userProfile, Optional username, + UserClassification classification, UserAccess access, UserStatus status, Roles roles, Features features) { - return new Registered + return new Registered(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, Username = username.ValueOrDefault!, - Classification = classification.ToString(), - Access = access.ToString(), - Status = status.ToString(), + Classification = classification, + Access = access, + Status = status, Roles = roles.ToList(), - Features = features.ToList() + Features = features.ToList(), + UserProfile = new RegisteredUserProfile + { + FirstName = userProfile.Name.FirstName, + LastName = userProfile.Name.LastName.ValueOrDefault!, + Timezone = userProfile.Timezone.ToString(), + CountryCode = userProfile.Address.CountryCode.ToString() + } }; } } \ No newline at end of file diff --git a/src/EndUsersDomain/UserAccess.cs b/src/EndUsersDomain/UserAccess.cs deleted file mode 100644 index 0d8b52fa..00000000 --- a/src/EndUsersDomain/UserAccess.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace EndUsersDomain; - -public enum UserAccess -{ - Enabled = 0, - Suspended = 1 -} \ No newline at end of file diff --git a/src/EndUsersDomain/UserStatus.cs b/src/EndUsersDomain/UserStatus.cs deleted file mode 100644 index 7ee098dd..00000000 --- a/src/EndUsersDomain/UserStatus.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace EndUsersDomain; - -public enum UserStatus -{ - Unregistered = 0, - Registered = 1 -} \ No newline at end of file diff --git a/src/EndUsersInfrastructure.IntegrationTests/EndUsersApiSpec.cs b/src/EndUsersInfrastructure.IntegrationTests/EndUsersApiSpec.cs index 39fcd918..7e87de28 100644 --- a/src/EndUsersInfrastructure.IntegrationTests/EndUsersApiSpec.cs +++ b/src/EndUsersInfrastructure.IntegrationTests/EndUsersApiSpec.cs @@ -1,6 +1,8 @@ using ApiHost1; using Domain.Interfaces.Authorization; +using EndUsersInfrastructure.IntegrationTests.Stubs; using FluentAssertions; +using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Web.Api.Common.Extensions; using Infrastructure.Web.Api.Operations.Shared.EndUsers; using IntegrationTesting.WebApi.Common; @@ -13,9 +15,22 @@ namespace EndUsersInfrastructure.IntegrationTests; [Collection("API")] public class EndUsersApiSpec : WebApiSpec { + private readonly StubEventNotificationMessageBroker _messageBroker; + public EndUsersApiSpec(WebApiSetup setup) : base(setup, OverrideDependencies) { EmptyAllRepositories(); + _messageBroker = setup.GetRequiredService() + .As(); + _messageBroker.Reset(); + } + + [Fact] + public async Task WhenRegisterUser_ThenPublishesRegistrationIntegrationEvent() + { + var login = await LoginUserAsync(); + + _messageBroker.LastPublishedEvent!.RootId.Should().Be(login.User.Id); } [Fact] @@ -58,6 +73,6 @@ await Api.PostAsync(new AssignPlatformRolesRequest private static void OverrideDependencies(IServiceCollection services) { - // Override dependencies here + services.AddSingleton(); } } \ No newline at end of file diff --git a/src/EndUsersInfrastructure.IntegrationTests/Stubs/StubEventNotificationMessageBroker.cs b/src/EndUsersInfrastructure.IntegrationTests/Stubs/StubEventNotificationMessageBroker.cs new file mode 100644 index 00000000..8bde2dbd --- /dev/null +++ b/src/EndUsersInfrastructure.IntegrationTests/Stubs/StubEventNotificationMessageBroker.cs @@ -0,0 +1,20 @@ +using Common; +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace EndUsersInfrastructure.IntegrationTests.Stubs; + +public class StubEventNotificationMessageBroker : IEventNotificationMessageBroker +{ + public IIntegrationEvent? LastPublishedEvent { get; private set; } + + public Task> PublishAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken) + { + LastPublishedEvent = integrationEvent; + return Task.FromResult(Result.Ok); + } + + public void Reset() + { + LastPublishedEvent = null; + } +} \ No newline at end of file diff --git a/src/EndUsersInfrastructure/ApplicationServices/EndUsersInProcessServiceClient.cs b/src/EndUsersInfrastructure/ApplicationServices/EndUsersInProcessServiceClient.cs index 476013cf..97c1fc31 100644 --- a/src/EndUsersInfrastructure/ApplicationServices/EndUsersInProcessServiceClient.cs +++ b/src/EndUsersInfrastructure/ApplicationServices/EndUsersInProcessServiceClient.cs @@ -22,13 +22,6 @@ public EndUsersInProcessServiceClient(IEndUsersApplication endUsersApplication, _invitationsApplication = invitationsApplication; } - public async Task> CreateMembershipForCallerPrivateAsync(ICallerContext caller, - string organizationId, - CancellationToken cancellationToken) - { - return await _endUsersApplication.CreateMembershipForCallerAsync(caller, organizationId, cancellationToken); - } - public async Task, Error>> FindPersonByEmailPrivateAsync(ICallerContext caller, string emailAddress, CancellationToken cancellationToken) { diff --git a/src/EndUsersInfrastructure/EndUsersInfrastructure.csproj b/src/EndUsersInfrastructure/EndUsersInfrastructure.csproj index 347e15f7..e3a8d49f 100644 --- a/src/EndUsersInfrastructure/EndUsersInfrastructure.csproj +++ b/src/EndUsersInfrastructure/EndUsersInfrastructure.csproj @@ -20,6 +20,7 @@ + diff --git a/src/EndUsersInfrastructure/EndUsersModule.cs b/src/EndUsersInfrastructure/EndUsersModule.cs index e69a8bb6..a6d39587 100644 --- a/src/EndUsersInfrastructure/EndUsersModule.cs +++ b/src/EndUsersInfrastructure/EndUsersModule.cs @@ -11,9 +11,12 @@ using EndUsersDomain; using EndUsersInfrastructure.Api.EndUsers; using EndUsersInfrastructure.ApplicationServices; +using EndUsersInfrastructure.Notifications; using EndUsersInfrastructure.Persistence; using EndUsersInfrastructure.Persistence.ReadModels; +using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Hosting.Common.Extensions; +using Infrastructure.Interfaces; using Infrastructure.Persistence.Interfaces; using Infrastructure.Web.Hosting.Common; using Microsoft.AspNetCore.Builder; @@ -50,7 +53,6 @@ public Action RegisterServices c.GetRequiredService(), c.GetRequiredServiceForPlatform(), c.GetRequiredService(), - c.GetRequiredService(), c.GetRequiredService(), c.GetRequiredService(), c.GetRequiredService())); @@ -71,10 +73,15 @@ public Action RegisterServices c.GetRequiredService(), c.GetRequiredService>(), c.GetRequiredServiceForPlatform())); - services.RegisterUnTenantedEventing( + services + .AddPerHttpRequest(c => + new EndUserDomainNotificationConsumer(c.GetRequiredService(), + c.GetRequiredService())); + services.RegisterUnTenantedEventing( c => new EndUserProjection(c.GetRequiredService(), c.GetRequiredService(), - c.GetRequiredServiceForPlatform())); + c.GetRequiredServiceForPlatform()), + c => new EndUserNotifier(c.GetRequiredService>())); services.AddSingleton(); }; diff --git a/src/EndUsersInfrastructure/Notifications/EndUserDomainNotificationConsumer.cs b/src/EndUsersInfrastructure/Notifications/EndUserDomainNotificationConsumer.cs new file mode 100644 index 00000000..9f8205b9 --- /dev/null +++ b/src/EndUsersInfrastructure/Notifications/EndUserDomainNotificationConsumer.cs @@ -0,0 +1,34 @@ +using Common; +using Domain.Events.Shared.Organizations; +using Domain.Interfaces.Entities; +using EndUsersApplication; +using Infrastructure.Eventing.Interfaces.Notifications; +using Infrastructure.Interfaces; + +namespace EndUsersInfrastructure.Notifications; + +public class EndUserDomainNotificationConsumer : IDomainEventNotificationConsumer +{ + private readonly ICallerContextFactory _callerContextFactory; + private readonly IEndUsersApplication _endUsersApplication; + + public EndUserDomainNotificationConsumer(ICallerContextFactory callerContextFactory, + IEndUsersApplication endUsersApplication) + { + _callerContextFactory = callerContextFactory; + _endUsersApplication = endUsersApplication; + } + + public async Task> NotifyAsync(IDomainEvent domainEvent, CancellationToken cancellationToken) + { + switch (domainEvent) + { + case Created created: + return await _endUsersApplication.HandleOrganizationCreatedAsync(_callerContextFactory.Create(), + created, cancellationToken); + + default: + return Result.Ok; + } + } +} \ No newline at end of file diff --git a/src/EndUsersInfrastructure/Notifications/EndUserIntegrationEventNotificationTranslator.cs b/src/EndUsersInfrastructure/Notifications/EndUserIntegrationEventNotificationTranslator.cs new file mode 100644 index 00000000..a89632a9 --- /dev/null +++ b/src/EndUsersInfrastructure/Notifications/EndUserIntegrationEventNotificationTranslator.cs @@ -0,0 +1,36 @@ +using Common; +using Domain.Events.Shared.EndUsers; +using Domain.Interfaces.Entities; +using Infrastructure.Eventing.Interfaces.Notifications; +using Integration.Events.Shared.EndUsers; + +namespace EndUsersInfrastructure.Notifications; + +/// +/// Provides an example translator of domain events that should be published as integration events +/// +public sealed class + EndUserIntegrationEventNotificationTranslator : IIntegrationEventNotificationTranslator + where TAggregateRoot : IEventingAggregateRoot +{ + public Type RootAggregateType => typeof(TAggregateRoot); + + public async Task, Error>> TranslateAsync(IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + await Task.CompletedTask; + switch (domainEvent) + { + case Registered registered: + return new PersonRegistered(registered.RootId) + { + Features = registered.Features, + Roles = registered.Roles, + Username = registered.Username ?? string.Empty, + UserProfile = registered.UserProfile + }.ToOptional(); + } + + return Optional.None; + } +} \ No newline at end of file diff --git a/src/EndUsersInfrastructure/Notifications/EndUserNotifier.cs b/src/EndUsersInfrastructure/Notifications/EndUserNotifier.cs new file mode 100644 index 00000000..a4078e26 --- /dev/null +++ b/src/EndUsersInfrastructure/Notifications/EndUserNotifier.cs @@ -0,0 +1,17 @@ +using EndUsersDomain; +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace EndUsersInfrastructure.Notifications; + +public class EndUserNotifier : IEventNotificationRegistration +{ + public EndUserNotifier(IEnumerable domainConsumers) + { + DomainEventConsumers = domainConsumers.ToList(); + } + + public List DomainEventConsumers { get; } + + public IIntegrationEventNotificationTranslator IntegrationEventTranslator => + new EndUserIntegrationEventNotificationTranslator(); +} \ No newline at end of file diff --git a/src/EndUsersInfrastructure/Persistence/EndUserRepository.cs b/src/EndUsersInfrastructure/Persistence/EndUserRepository.cs index 0931c3ac..6f8b218c 100644 --- a/src/EndUsersInfrastructure/Persistence/EndUserRepository.cs +++ b/src/EndUsersInfrastructure/Persistence/EndUserRepository.cs @@ -4,6 +4,7 @@ using Common; using Domain.Common.ValueObjects; using Domain.Interfaces; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence; using EndUsersApplication.Persistence.ReadModels; using EndUsersDomain; @@ -49,6 +50,12 @@ public async Task> LoadAsync(Identifier id, Cancellat } public async Task> SaveAsync(EndUserRoot user, CancellationToken cancellationToken) + { + return await SaveAsync(user, false, cancellationToken); + } + + public async Task> SaveAsync(EndUserRoot user, bool reload, + CancellationToken cancellationToken) { var saved = await _users.SaveAsync(user, cancellationToken); if (!saved.IsSuccessful) @@ -56,7 +63,9 @@ public async Task> SaveAsync(EndUserRoot user, Cancel return saved.Error; } - return user; + return reload + ? await LoadAsync(user.Id, cancellationToken) + : user; } public async Task, Error>> SearchAllMembershipsByOrganizationAsync( @@ -72,7 +81,7 @@ public async Task, Error>> SearchAllMember .Select(mje => mje.IsDefault) .Select(mje => mje.LastPersistedAtUtc) .SelectFromJoin(mje => mje.InvitedEmailAddress, inv => inv.InvitedEmailAddress) - .SelectFromJoin(mje => mje.Status, inv => inv.Status) + .SelectFromJoin(mje => mje.Status, inv => inv.Status) .OrderBy(mje => mje.LastPersistedAtUtc) .WithSearchOptions(searchOptions); diff --git a/src/EndUsersInfrastructure/Persistence/InvitationRepository.cs b/src/EndUsersInfrastructure/Persistence/InvitationRepository.cs index cf36e0e9..7ad07597 100644 --- a/src/EndUsersInfrastructure/Persistence/InvitationRepository.cs +++ b/src/EndUsersInfrastructure/Persistence/InvitationRepository.cs @@ -4,6 +4,7 @@ using Domain.Common.ValueObjects; using Domain.Interfaces; using Domain.Shared; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence; using EndUsersApplication.Persistence.ReadModels; using EndUsersDomain; @@ -37,7 +38,7 @@ public async Task, Error>> FindInvitedGuestByEmailA { var query = Query.From() .Where(eu => eu.InvitedEmailAddress, ConditionOperator.EqualTo, emailAddress.Address) - .AndWhere(eu => eu.Status, ConditionOperator.EqualTo, UserStatus.Unregistered.ToString()); + .AndWhere(eu => eu.Status, ConditionOperator.EqualTo, UserStatus.Unregistered); return await FindFirstByQueryAsync(query, cancellationToken); } @@ -46,7 +47,7 @@ public async Task, Error>> FindInvitedGuestByTokenA { var query = Query.From() .Where(eu => eu.Token, ConditionOperator.EqualTo, token) - .AndWhere(eu => eu.Status, ConditionOperator.EqualTo, UserStatus.Unregistered.ToString()); + .AndWhere(eu => eu.Status, ConditionOperator.EqualTo, UserStatus.Unregistered); return await FindFirstByQueryAsync(query, cancellationToken); } diff --git a/src/EndUsersInfrastructure/Persistence/ReadModels/EndUserProjection.cs b/src/EndUsersInfrastructure/Persistence/ReadModels/EndUserProjection.cs index cefc777f..244981fd 100644 --- a/src/EndUsersInfrastructure/Persistence/ReadModels/EndUserProjection.cs +++ b/src/EndUsersInfrastructure/Persistence/ReadModels/EndUserProjection.cs @@ -6,6 +6,7 @@ using Domain.Interfaces; using Domain.Interfaces.Entities; using Domain.Shared; +using Domain.Shared.EndUsers; using EndUsersApplication.Persistence.ReadModels; using EndUsersDomain; using Infrastructure.Persistence.Common; @@ -170,7 +171,7 @@ public async Task> ProjectEventAsync(IDomainEvent changeEven return await _invitations.HandleUpdateAsync(e.RootId.ToId(), dto => { dto.Token = Optional.None; - dto.Status = UserStatus.Registered.ToString(); + dto.Status = UserStatus.Registered; dto.AcceptedAtUtc = e.AcceptedAtUtc; dto.AcceptedEmailAddress = e.AcceptedEmailAddress; }, cancellationToken); diff --git a/src/IdentityDomain/Events.cs b/src/IdentityDomain/Events.cs index 42b6a3ac..e2fb5c12 100644 --- a/src/IdentityDomain/Events.cs +++ b/src/IdentityDomain/Events.cs @@ -14,11 +14,9 @@ public static class AuthTokens { public static Created Created(Identifier id, Identifier userId) { - return new Created + return new Created(id) { - RootId = id, - UserId = userId, - OccurredUtc = DateTime.UtcNow + UserId = userId }; } @@ -26,15 +24,13 @@ public static TokensChanged TokensChanged(Identifier id, Identifier userId, stri DateTime accessTokenExpiresOn, string refreshToken, DateTime refreshTokenExpiresOn) { - return new TokensChanged + return new TokensChanged(id) { - RootId = id, UserId = userId, AccessToken = accessToken, RefreshToken = refreshToken, AccessTokenExpiresOn = accessTokenExpiresOn, - RefreshTokenExpiresOn = refreshTokenExpiresOn, - OccurredUtc = DateTime.UtcNow + RefreshTokenExpiresOn = refreshTokenExpiresOn }; } @@ -42,25 +38,21 @@ public static TokensRefreshed TokensRefreshed(Identifier id, Identifier userId, DateTime accessTokenExpiresOn, string refreshToken, DateTime refreshTokenExpiresOn) { - return new TokensRefreshed + return new TokensRefreshed(id) { - RootId = id, UserId = userId, AccessToken = accessToken, RefreshToken = refreshToken, AccessTokenExpiresOn = accessTokenExpiresOn, - RefreshTokenExpiresOn = refreshTokenExpiresOn, - OccurredUtc = DateTime.UtcNow + RefreshTokenExpiresOn = refreshTokenExpiresOn }; } public static TokensRevoked TokensRevoked(Identifier id, Identifier userId) { - return new TokensRevoked + return new TokensRevoked(id) { - RootId = id, - UserId = userId, - OccurredUtc = DateTime.UtcNow + UserId = userId }; } } @@ -69,105 +61,79 @@ public static class PasswordCredentials { public static AccountLocked AccountLocked(Identifier id) { - return new AccountLocked - { - RootId = id, - OccurredUtc = DateTime.UtcNow - }; + return new AccountLocked(id); } public static AccountUnlocked AccountUnlocked(Identifier id) { - return new AccountUnlocked - { - RootId = id, - OccurredUtc = DateTime.UtcNow - }; + return new AccountUnlocked(id); } public static Domain.Events.Shared.Identities.PasswordCredentials.Created Created(Identifier id, Identifier userId) { - return new Domain.Events.Shared.Identities.PasswordCredentials.Created + return new Domain.Events.Shared.Identities.PasswordCredentials.Created(id) { - RootId = id, - UserId = userId, - OccurredUtc = DateTime.UtcNow + UserId = userId }; } public static CredentialsChanged CredentialsChanged(Identifier id, string passwordHash) { - return new CredentialsChanged + return new CredentialsChanged(id) { - RootId = id, - PasswordHash = passwordHash, - OccurredUtc = DateTime.UtcNow + PasswordHash = passwordHash }; } public static PasswordResetCompleted PasswordResetCompleted(Identifier id, string token, string passwordHash) { - return new PasswordResetCompleted + return new PasswordResetCompleted(id) { - RootId = id, Token = token, - PasswordHash = passwordHash, - OccurredUtc = DateTime.UtcNow + PasswordHash = passwordHash }; } public static PasswordResetInitiated PasswordResetInitiated(Identifier id, string token) { - return new PasswordResetInitiated + return new PasswordResetInitiated(id) { - RootId = id, - Token = token, - OccurredUtc = DateTime.UtcNow + Token = token }; } public static PasswordVerified PasswordVerified(Identifier id, bool isVerified, bool auditAttempt) { - return new PasswordVerified + return new PasswordVerified(id) { - RootId = id, IsVerified = isVerified, - AuditAttempt = auditAttempt, - OccurredUtc = DateTime.UtcNow + AuditAttempt = auditAttempt }; } public static RegistrationChanged RegistrationChanged(Identifier id, EmailAddress emailAddress, PersonDisplayName name) { - return new RegistrationChanged + return new RegistrationChanged(id) { - RootId = id, EmailAddress = emailAddress, - Name = name, - OccurredUtc = DateTime.UtcNow + Name = name }; } public static RegistrationVerificationCreated RegistrationVerificationCreated(Identifier id, string token) { - return new RegistrationVerificationCreated + return new RegistrationVerificationCreated(id) { - RootId = id, - Token = token, - OccurredUtc = DateTime.UtcNow + Token = token }; } public static RegistrationVerificationVerified RegistrationVerificationVerified(Identifier id) { - return new RegistrationVerificationVerified - { - RootId = id, - OccurredUtc = DateTime.UtcNow - }; + return new RegistrationVerificationVerified(id); } } @@ -176,35 +142,29 @@ public static class APIKeys public static Domain.Events.Shared.Identities.APIKeys.Created Created(Identifier id, Identifier userId, string keyToken, string keyHash) { - return new Domain.Events.Shared.Identities.APIKeys.Created + return new Domain.Events.Shared.Identities.APIKeys.Created(id) { - RootId = id, UserId = userId, KeyToken = keyToken, - KeyHash = keyHash, - OccurredUtc = DateTime.UtcNow + KeyHash = keyHash }; } public static KeyVerified KeyVerified(Identifier id, bool isVerified) { - return new KeyVerified + return new KeyVerified(id) { - RootId = id, - IsVerified = isVerified, - OccurredUtc = DateTime.UtcNow + IsVerified = isVerified }; } public static ParametersChanged ParametersChanged(Identifier id, string description, DateTime expiresOn) { - return new ParametersChanged + return new ParametersChanged(id) { - RootId = id, Description = description, - ExpiresOn = expiresOn, - OccurredUtc = DateTime.UtcNow + ExpiresOn = expiresOn }; } } @@ -214,28 +174,24 @@ public static class SSOUsers public static Domain.Events.Shared.Identities.SSOUsers.Created Created(Identifier id, string providerName, Identifier userId) { - return new Domain.Events.Shared.Identities.SSOUsers.Created + return new Domain.Events.Shared.Identities.SSOUsers.Created(id) { - RootId = id, ProviderName = providerName, - UserId = userId, - OccurredUtc = DateTime.UtcNow + UserId = userId }; } public static TokensUpdated TokensUpdated(Identifier id, string tokens, EmailAddress emailAddress, PersonName name, Timezone timezone, Address address) { - return new TokensUpdated + return new TokensUpdated(id) { - RootId = id, Tokens = tokens, EmailAddress = emailAddress, FirstName = name.FirstName, LastName = name.LastName.ValueOrDefault?.Text, Timezone = timezone.Code.ToString(), - CountryCode = address.CountryCode.ToString(), - OccurredUtc = DateTime.UtcNow + CountryCode = address.CountryCode.ToString() }; } } diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj b/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj index 6ec2b4e8..763395c7 100644 --- a/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj +++ b/src/Infrastructure.Eventing.Common.UnitTests/Infrastructure.Eventing.Common.UnitTests.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Notifications/EventNotificationNotifierSpec.cs b/src/Infrastructure.Eventing.Common.UnitTests/Notifications/EventNotificationNotifierSpec.cs index 9bbc7255..af83b570 100644 --- a/src/Infrastructure.Eventing.Common.UnitTests/Notifications/EventNotificationNotifierSpec.cs +++ b/src/Infrastructure.Eventing.Common.UnitTests/Notifications/EventNotificationNotifierSpec.cs @@ -1,6 +1,5 @@ using Application.Persistence.Interfaces; using Common; -using Common.Extensions; using Domain.Common; using Domain.Common.Extensions; using Domain.Common.ValueObjects; @@ -17,6 +16,8 @@ namespace Infrastructure.Eventing.Common.UnitTests.Notifications; [Trait("Category", "Unit")] public sealed class EventNotificationNotifierSpec : IDisposable { + private readonly Mock _domainConsumer; + private readonly Mock _messageBroker; private readonly EventNotificationNotifier _notifier; private readonly Mock _registration; @@ -25,14 +26,22 @@ public EventNotificationNotifierSpec() var recorder = new Mock(); var changeEventTypeMigrator = new ChangeEventTypeMigrator(); _registration = new Mock(); - _registration.Setup(p => p.Producer.RootAggregateType) + _registration.Setup(p => p.IntegrationEventTranslator.RootAggregateType) .Returns(typeof(string)); - _registration.Setup(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny())) - .Returns(Task.FromResult, Error>>(Optional.None)); - _registration.Setup(p => p.Consumer.NotifyAsync(It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(true)); + _registration.Setup(p => + p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Optional.None); + _domainConsumer = new Mock(); + _domainConsumer.Setup(c => c.NotifyAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Ok); + _messageBroker = new Mock(); + _messageBroker.Setup(mb => mb.PublishAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Ok); + _registration.Setup(p => p.DomainEventConsumers) + .Returns([_domainConsumer.Object]); var registrations = new List { _registration.Object }; - _notifier = new EventNotificationNotifier(recorder.Object, changeEventTypeMigrator, registrations.ToArray()); + _notifier = new EventNotificationNotifier(recorder.Object, changeEventTypeMigrator, registrations, + _messageBroker.Object); } ~EventNotificationNotifierSpec() @@ -57,25 +66,29 @@ private void Dispose(bool disposing) [Fact] public async Task WhenWriteEventStreamAndNoEvents_ThenReturns() { - await _notifier.WriteEventStreamAsync("astreamname", new List(), + await _notifier.WriteEventStreamAsync("astreamname", [], CancellationToken.None); - _registration.Verify(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny()), + _domainConsumer.Verify(c => c.NotifyAsync(It.IsAny(), It.IsAny()), + Times.Never); + _messageBroker.Verify(mb => mb.PublishAsync(It.IsAny(), It.IsAny()), + Times.Never); + _registration.Verify( + p => p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny()), Times.Never); } [Fact] - public async Task WhenWriteEventStreamAndNoConfiguredConsumer_ThenReturns() + public async Task WhenWriteEventStreamAndNoRegisteredConsumers_ThenReturns() { - _registration.Setup(p => p.Producer.RootAggregateType) + _registration.Setup(p => p.IntegrationEventTranslator.RootAggregateType) .Returns(typeof(string)); - var result = await _notifier.WriteEventStreamAsync("astreamname", new List - { - new() + var result = await _notifier.WriteEventStreamAsync("astreamname", [ + new EventStreamChangeEvent { Data = null!, - EntityType = "atypename", + RootAggregateType = "atypename", EventType = null!, Id = null!, LastPersistedAtUtc = default, @@ -83,23 +96,27 @@ public async Task WhenWriteEventStreamAndNoConfiguredConsumer_ThenReturns() StreamName = null!, Version = 0 } - }, CancellationToken.None); + ], CancellationToken.None); result.Should().BeSuccess(); - _registration.Verify(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny()), + _domainConsumer.Verify(c => c.NotifyAsync(It.IsAny(), It.IsAny()), + Times.Never); + _messageBroker.Verify(mb => mb.PublishAsync(It.IsAny(), It.IsAny()), + Times.Never); + _registration.Verify( + p => p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task WhenWriteEventStreamAndDeserializationOfEventsFails_ThenReturnsError() { - var result = await _notifier.WriteEventStreamAsync("astreamname", new List - { - new() + var result = await _notifier.WriteEventStreamAsync("astreamname", [ + new EventStreamChangeEvent { Id = "anid", - EntityType = nameof(String), - Data = new TestChangeEvent + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid" }.ToEventJson(), @@ -109,200 +126,187 @@ public async Task WhenWriteEventStreamAndDeserializationOfEventsFails_ThenReturn LastPersistedAtUtc = default, StreamName = null! } - }, CancellationToken.None); + ], CancellationToken.None); result.Should().BeError(ErrorCode.RuleViolation); } [Fact] - public async Task WhenWriteEventStreamAndProducerDoesNotPublishEvent_ThenReturns() + public async Task WhenWriteEventStreamAndTranslatorDoesNotTranslateEvent_ThenOnlyNotifiesDomainEvent() { - _registration.Setup(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny())) - .Returns((IDomainEvent _, CancellationToken _) => - Task.FromResult, Error>>(Optional.None)); + _registration.Setup(p => + p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync((IDomainEvent _, CancellationToken _) => Optional.None); - var result = await _notifier.WriteEventStreamAsync("astreamname", new List - { - new() + var result = await _notifier.WriteEventStreamAsync("astreamname", [ + new EventStreamChangeEvent { Id = "anid1", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid1" }.ToEventJson(), + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid" }.ToEventJson(), Version = 0, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), + Metadata = new EventMetadata(typeof(TestDomainEvent).AssemblyQualifiedName!), EventType = null!, LastPersistedAtUtc = default, StreamName = null! } - }, CancellationToken.None); + ], CancellationToken.None); result.Should().BeSuccess(); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => - e.RootId == "aneventid1" + _registration.Verify(p => p.IntegrationEventTranslator.TranslateAsync(It.Is(e => + e.RootId == "aneventid" ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.IsAny(), It.IsAny()), + _domainConsumer.Verify(c => c.NotifyAsync(It.Is(ce => + ce.RootId == "aneventid" + ), It.IsAny())); + _messageBroker.Verify(mb => mb.PublishAsync(It.IsAny(), It.IsAny()), Times.Never); } [Fact] - public async Task WhenWriteEventStreamAndFirstEverEvent_ThenNotifiesEvents() + public async Task WhenWriteEventStreamWithSingleEvent_ThenNotifiesBothDomainAndIntegrationEvents() { - _registration.Setup(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny())) - .Returns((IDomainEvent @event, CancellationToken _) => - Task.FromResult, Error>>(new TestChangeEvent { RootId = @event.RootId } - .ToOptional())); + _registration.Setup(p => + p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync((IDomainEvent domainEvent, CancellationToken _) => + new TestIntegrationEvent(domainEvent.RootId).ToOptional()); - var result = await _notifier.WriteEventStreamAsync("astreamname", new List - { - new() + var result = await _notifier.WriteEventStreamAsync("astreamname", [ + new EventStreamChangeEvent { Id = "anid1", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid1" }.ToEventJson(), + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid" }.ToEventJson(), Version = 0, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), - EventType = null!, - LastPersistedAtUtc = default, - StreamName = null! - }, - new() - { - Id = "anid2", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid2" }.ToEventJson(), - Version = 1, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), - EventType = null!, - LastPersistedAtUtc = default, - StreamName = null! - }, - new() - { - Id = "anid3", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid3" }.ToEventJson(), - Version = 2, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), + Metadata = new EventMetadata(typeof(TestDomainEvent).AssemblyQualifiedName!), EventType = null!, LastPersistedAtUtc = default, StreamName = null! } - }, CancellationToken.None); + ], CancellationToken.None); result.Should().BeSuccess(); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => - e.RootId == "aneventid2" + _registration.Verify(p => p.IntegrationEventTranslator.TranslateAsync(It.Is(e => + e.RootId == "aneventid" ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.Is(e => - e.RootId == "aneventid2" + _domainConsumer.Verify(c => c.NotifyAsync(It.Is(ce => + ce.RootId == "aneventid" ), It.IsAny())); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => - e.RootId == "aneventid3" - ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.Is(e => - e.RootId == "aneventid3" + _messageBroker.Verify(mb => mb.PublishAsync(It.Is(ie => + ie.RootId == "aneventid" ), It.IsAny())); } [Fact] - public async Task WhenWriteEventStreamAndConsumerDoesNotHandleEvent_ThenReturnsError() + public async Task WhenWriteEventStreamWithMultipleEvents_ThenNotifiesBothDomainAndIntegrationEvents() { - _registration.Setup(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny())) - .Returns((IDomainEvent @event, CancellationToken _) => - Task.FromResult, Error>>(new TestChangeEvent { RootId = @event.RootId } - .ToOptional())); - _registration.Setup(p => p.Consumer.NotifyAsync(It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(false)); + _registration.Setup(p => + p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync((IDomainEvent domainEvent, CancellationToken _) => + new TestIntegrationEvent(domainEvent.RootId).ToOptional()); - var result = await _notifier.WriteEventStreamAsync("astreamname", new List - { - new() + var result = await _notifier.WriteEventStreamAsync("astreamname", [ + new EventStreamChangeEvent { Id = "anid1", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid1" }.ToEventJson(), + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid1" }.ToEventJson(), Version = 0, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), - EventType = null!, - LastPersistedAtUtc = default, - StreamName = null! - } - }, CancellationToken.None); - - result.Should().BeError(ErrorCode.RuleViolation, - Resources.EventNotificationNotifier_ConsumerError.Format("IEventNotificationConsumerProxy", "anid1", - typeof(TestChangeEvent).AssemblyQualifiedName!)); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => - e.RootId == "aneventid1" - ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.Is(e => - e.RootId == "aneventid1" - ), It.IsAny())); - } - - [Fact] - public async Task WhenWriteEventStream_ThenNotifiesEvents() - { - _registration.Setup(p => p.Producer.PublishAsync(It.IsAny(), It.IsAny())) - .Returns((IDomainEvent @event, CancellationToken _) => - Task.FromResult, Error>>(new TestChangeEvent { RootId = @event.RootId } - .ToOptional())); - - var result = await _notifier.WriteEventStreamAsync("astreamname", new List - { - new() - { - Id = "anid1", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid1" }.ToEventJson(), - Version = 3, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), + Metadata = new EventMetadata(typeof(TestDomainEvent).AssemblyQualifiedName!), EventType = null!, LastPersistedAtUtc = default, StreamName = null! }, - new() + + new EventStreamChangeEvent { Id = "anid2", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid2" }.ToEventJson(), - Version = 4, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid2" }.ToEventJson(), + Version = 1, + Metadata = new EventMetadata(typeof(TestDomainEvent).AssemblyQualifiedName!), EventType = null!, LastPersistedAtUtc = default, StreamName = null! }, - new() + + new EventStreamChangeEvent { Id = "anid3", - EntityType = nameof(String), - Data = new TestChangeEvent { RootId = "aneventid3" }.ToEventJson(), - Version = 5, - Metadata = new EventMetadata(typeof(TestChangeEvent).AssemblyQualifiedName!), + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid3" }.ToEventJson(), + Version = 2, + Metadata = new EventMetadata(typeof(TestDomainEvent).AssemblyQualifiedName!), EventType = null!, LastPersistedAtUtc = default, StreamName = null! } - }, CancellationToken.None); + ], CancellationToken.None); result.Should().BeSuccess(); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => + _registration.Verify(p => p.IntegrationEventTranslator.TranslateAsync(It.Is(e => + e.RootId == "aneventid1" + ), It.IsAny())); + _domainConsumer.Verify(c => c.NotifyAsync(It.Is(e => e.RootId == "aneventid1" ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.Is(e => + _messageBroker.Verify(mb => mb.PublishAsync(It.Is(e => e.RootId == "aneventid1" ), It.IsAny())); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => + _registration.Verify(p => p.IntegrationEventTranslator.TranslateAsync(It.Is(e => e.RootId == "aneventid2" ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.Is(e => + _domainConsumer.Verify(c => c.NotifyAsync(It.Is(e => e.RootId == "aneventid2" ), It.IsAny())); - _registration.Verify(p => p.Producer.PublishAsync(It.Is(e => + _messageBroker.Verify(mb => mb.PublishAsync(It.Is(e => + e.RootId == "aneventid2" + ), It.IsAny())); + _registration.Verify(r => r.IntegrationEventTranslator.TranslateAsync(It.Is(e => + e.RootId == "aneventid3" + ), It.IsAny())); + _domainConsumer.Verify(c => c.NotifyAsync(It.Is(e => e.RootId == "aneventid3" ), It.IsAny())); - _registration.Verify(p => p.Consumer.NotifyAsync(It.Is(e => + _messageBroker.Verify(mb => mb.PublishAsync(It.Is(e => e.RootId == "aneventid3" ), It.IsAny())); } + + [Fact] + public async Task WhenWriteEventStreamAndDomainConsumerReturnsError_ThenStopsAndReturnsError() + { + _registration.Setup(p => + p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync((IDomainEvent domainEvent, CancellationToken _) => + new TestIntegrationEvent(domainEvent.RootId).ToOptional()); + _domainConsumer.Setup(c => c.NotifyAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Error.RuleViolation("amessage")); + + var result = await _notifier.WriteEventStreamAsync("astreamname", [ + new EventStreamChangeEvent + { + Id = "anid1", + RootAggregateType = nameof(String), + Data = new TestDomainEvent { RootId = "aneventid" }.ToEventJson(), + Version = 0, + Metadata = new EventMetadata(typeof(TestDomainEvent).AssemblyQualifiedName!), + EventType = null!, + LastPersistedAtUtc = default, + StreamName = null! + } + ], CancellationToken.None); + + result.Should().BeError(ErrorCode.RuleViolation, "amessage"); + _domainConsumer.Verify(c => c.NotifyAsync(It.Is(e => + e.RootId == "aneventid" + ), It.IsAny())); + _registration.Verify( + p => p.IntegrationEventTranslator.TranslateAsync(It.IsAny(), It.IsAny()), + Times.Never); + _messageBroker.Verify(mb => mb.PublishAsync(It.IsAny(), It.IsAny()), + Times.Never); + + } + } \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestChangeEvent.cs b/src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestChangeEvent.cs deleted file mode 100644 index 977bff25..00000000 --- a/src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestChangeEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Domain.Interfaces.Entities; - -namespace Infrastructure.Eventing.Common.UnitTests.Notifications; - -public class TestChangeEvent : IDomainEvent -{ - public DateTime OccurredUtc { get; set; } - - public string RootId { get; set; } = "arootid"; -} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestDomainEvent.cs b/src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestDomainEvent.cs new file mode 100644 index 00000000..aa4905e1 --- /dev/null +++ b/src/Infrastructure.Eventing.Common.UnitTests/Notifications/TestDomainEvent.cs @@ -0,0 +1,22 @@ +using Domain.Common; +using Infrastructure.Eventing.Common.Notifications; + +namespace Infrastructure.Eventing.Common.UnitTests.Notifications; + +public class TestDomainEvent : DomainEvent +{ + public TestDomainEvent() : base("arootid") + { + } +} + +public class TestIntegrationEvent : IntegrationEvent +{ + public TestIntegrationEvent() : base("arootid") + { + } + + public TestIntegrationEvent(string rootId) : base(rootId) + { + } +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Projections/ReadModelProjectorSpec.cs b/src/Infrastructure.Eventing.Common.UnitTests/Projections/ReadModelProjectorSpec.cs index 79f579b2..15c0735e 100644 --- a/src/Infrastructure.Eventing.Common.UnitTests/Projections/ReadModelProjectorSpec.cs +++ b/src/Infrastructure.Eventing.Common.UnitTests/Projections/ReadModelProjectorSpec.cs @@ -81,7 +81,7 @@ public async Task WhenWriteEventStreamAsyncAndNoConfiguredProjection_ThenReturns new() { Data = null!, - EntityType = "atypename", + RootAggregateType = "atypename", EventType = null!, Id = null!, LastPersistedAtUtc = default, @@ -117,7 +117,7 @@ public async Task WhenWriteEventStreamAsyncAndEventVersionGreaterThanCheckpoint_ new() { Data = null!, - EntityType = nameof(String), + RootAggregateType = nameof(String), EventType = null!, Id = null!, LastPersistedAtUtc = default, @@ -141,7 +141,7 @@ public async Task WhenWriteEventStreamAsyncAndEventVersionLessThanCheckpoint_The new() { Id = "anid1", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid1" }.ToEventJson(), Version = 4, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -152,7 +152,7 @@ public async Task WhenWriteEventStreamAsyncAndEventVersionLessThanCheckpoint_The new() { Id = "anid2", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid2" }.ToEventJson(), Version = 5, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -163,7 +163,7 @@ public async Task WhenWriteEventStreamAsyncAndEventVersionLessThanCheckpoint_The new() { Id = "anid3", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid3" }.ToEventJson(), Version = 6, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -198,7 +198,7 @@ public async Task WhenWriteEventStreamAsyncAndDeserializationOfEventsFails_ThenR { Id = "anid", LastPersistedAtUtc = default, - EntityType = nameof(String), + RootAggregateType = nameof(String), EventType = null!, Data = new TestEvent { @@ -225,7 +225,7 @@ public async Task WhenWriteEventStreamAsyncAndFirstEverEvent_ThenProjectsEvents( new() { Id = "anid1", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid1" }.ToEventJson(), Version = startingCheckpoint, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -236,7 +236,7 @@ public async Task WhenWriteEventStreamAsyncAndFirstEverEvent_ThenProjectsEvents( new() { Id = "anid2", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid2" }.ToEventJson(), Version = startingCheckpoint + 1, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -247,7 +247,7 @@ public async Task WhenWriteEventStreamAsyncAndFirstEverEvent_ThenProjectsEvents( new() { Id = "anid3", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid3" }.ToEventJson(), Version = startingCheckpoint + 2, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -284,7 +284,7 @@ public async Task WhenWriteEventStreamAsyncAndEventNotHandledByProjection_ThenRe new() { Id = "anid1", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid1" }.ToEventJson(), Version = 3, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -317,7 +317,7 @@ public async Task WhenWriteEventStreamAsync_ThenProjectsEvents() new() { Id = "anid1", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid1" }.ToEventJson(), Version = 3, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -328,7 +328,7 @@ public async Task WhenWriteEventStreamAsync_ThenProjectsEvents() new() { Id = "anid2", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid2" }.ToEventJson(), Version = 4, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), @@ -339,7 +339,7 @@ public async Task WhenWriteEventStreamAsync_ThenProjectsEvents() new() { Id = "anid3", - EntityType = nameof(String), + RootAggregateType = nameof(String), Data = new TestEvent { RootId = "aneventid3" }.ToEventJson(), Version = 5, Metadata = new EventMetadata(typeof(TestEvent).AssemblyQualifiedName!), diff --git a/src/Infrastructure.Eventing.Common.UnitTests/Projections/TestEvent.cs b/src/Infrastructure.Eventing.Common.UnitTests/Projections/TestEvent.cs index bcc60ff6..ca9053c3 100644 --- a/src/Infrastructure.Eventing.Common.UnitTests/Projections/TestEvent.cs +++ b/src/Infrastructure.Eventing.Common.UnitTests/Projections/TestEvent.cs @@ -1,10 +1,10 @@ -using Domain.Interfaces.Entities; +using Domain.Common; namespace Infrastructure.Eventing.Common.UnitTests.Projections; -public class TestEvent : IDomainEvent +public class TestEvent : DomainEvent { - public DateTime OccurredUtc { get; set; } - - public string RootId { get; set; } = "arootid"; + public TestEvent() : base("arootid") + { + } } \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/EventNotificationNotifier.cs b/src/Infrastructure.Eventing.Common/Notifications/EventNotificationNotifier.cs index 87ee7948..32dd14f4 100644 --- a/src/Infrastructure.Eventing.Common/Notifications/EventNotificationNotifier.cs +++ b/src/Infrastructure.Eventing.Common/Notifications/EventNotificationNotifier.cs @@ -8,19 +8,21 @@ namespace Infrastructure.Eventing.Common.Notifications; /// -/// Provides a notifier of change events from registered producer to registered consumers +/// Provides a round-robin notifier of domain events to registered consumers /// public sealed class EventNotificationNotifier : IEventNotificationNotifier, IDisposable { + private readonly IEventNotificationMessageBroker _messageBroker; private readonly IEventSourcedChangeEventMigrator _migrator; private readonly IRecorder _recorder; public EventNotificationNotifier(IRecorder recorder, IEventSourcedChangeEventMigrator migrator, - params IEventNotificationRegistration[] registrations) + List registrations, IEventNotificationMessageBroker messageBroker) { _recorder = recorder; Registrations = registrations; _migrator = migrator; + _messageBroker = messageBroker; } ~EventNotificationNotifier() @@ -46,9 +48,7 @@ private void Dispose(bool disposing) foreach (var pair in Registrations) { // ReSharper disable once SuspiciousTypeConversion.Global - (pair.Producer as IDisposable)?.Dispose(); - // ReSharper disable once SuspiciousTypeConversion.Global - (pair.Consumer as IDisposable)?.Dispose(); + (pair.IntegrationEventTranslator as IDisposable)?.Dispose(); } } } @@ -70,77 +70,113 @@ public async Task> WriteEventStreamAsync(string streamName, List RelayEventStreamToAllConsumersInOrderAsync(registration, eventStream, cancellationToken))); + if (results.Any(r => !r.IsSuccessful)) + { + return results.First(r => !r.IsSuccessful).Error; + } + + return Result.Ok; + } + + private async Task> RelayEventStreamToAllConsumersInOrderAsync( + IEventNotificationRegistration registration, List eventStream, + CancellationToken cancellationToken) + { foreach (var changeEvent in eventStream) { - var deserialized = DeserializeEvent(changeEvent, _migrator); + var deserialized = DeserializeChangeEvent(changeEvent, _migrator); if (!deserialized.IsSuccessful) { return deserialized.Error; } - var relayed = await RelayEventAsync(registration.Value, deserialized.Value, changeEvent, - cancellationToken); - if (!relayed.IsSuccessful) + var @event = deserialized.Value; + var domainEventsRelayed = + await RelayDomainEventToAllConsumersAsync(registration, @event, cancellationToken); + if (!domainEventsRelayed.IsSuccessful) { - return relayed.Error; + return domainEventsRelayed.Error; + } + + var integrationEventsRelayed = + await RelayIntegrationEventToBrokerAsync(registration, changeEvent, @event, cancellationToken); + if (!integrationEventsRelayed.IsSuccessful) + { + return integrationEventsRelayed.Error; } } return Result.Ok; } - private async Task> RelayEventAsync(IEventNotificationRegistration registration, - IDomainEvent @event, EventStreamChangeEvent changeEvent, CancellationToken cancellationToken) + private static async Task> RelayDomainEventToAllConsumersAsync( + IEventNotificationRegistration registration, + IDomainEvent @event, CancellationToken cancellationToken) { - var published = await registration.Producer.PublishAsync(@event, cancellationToken); + if (registration.DomainEventConsumers.HasNone()) + { + return Result.Ok; + } + + var results = await Task.WhenAll(registration.DomainEventConsumers + .Select(consumer => consumer.NotifyAsync(@event, cancellationToken))); + if (results.Any(r => !r.IsSuccessful)) + { + return results.First(r => !r.IsSuccessful).Error; + } + + return Result.Ok; + } + + private async Task> RelayIntegrationEventToBrokerAsync( + IEventNotificationRegistration registration, EventStreamChangeEvent changeEvent, IDomainEvent @event, + CancellationToken cancellationToken) + { + var published = await registration.IntegrationEventTranslator.TranslateAsync(@event, cancellationToken); if (!published.IsSuccessful) { return published.Error.Wrap(Resources.EventNotificationNotifier_ProducerError.Format( - registration.Producer.GetType().Name, - changeEvent.Id, changeEvent.Metadata.Fqn)); + registration.IntegrationEventTranslator.GetType().Name, + @event, changeEvent.Metadata.Fqn)); } var publishedEvent = published.Value; if (!publishedEvent.HasValue) { _recorder.TraceInformation(null, - "The producer '{Producer}' chose not publish the event '{Event}' with event type '{Type}'", - registration.Producer.GetType().Name, - changeEvent.Id, changeEvent.Metadata.Fqn); + "The producer '{Producer}' chose not publish the integration event '{Event}' with event type '{Type}'", + registration.IntegrationEventTranslator.GetType().Name, changeEvent.Id, changeEvent.Metadata.Fqn); return Result.Ok; } - var notified = await registration.Consumer.NotifyAsync(publishedEvent.Value, cancellationToken); - if (!notified.IsSuccessful) - { - return notified.Error; - } - - if (!notified.Value) + var integrationEvent = publishedEvent.Value; + var brokered = await _messageBroker.PublishAsync(integrationEvent, cancellationToken); + if (!brokered.IsSuccessful) { - return Error.RuleViolation( - Resources.EventNotificationNotifier_ConsumerError.Format(registration.Consumer.GetType().Name, - changeEvent.Id, changeEvent.Metadata.Fqn)); + return brokered.Error; } return Result.Ok; } - private static Optional GetProducerForStream( - IEnumerable registrations, string entityTypeName) + private static List GetRegistrationsForStream( + IEnumerable registrations, string rootAggregateType) { - return new Optional( - registrations.FirstOrDefault(prj => prj.Producer.RootAggregateType.Name == entityTypeName)); + return registrations + .Where(prj => prj.IntegrationEventTranslator.RootAggregateType.Name == rootAggregateType) + .ToList(); } - private static Result DeserializeEvent(EventStreamChangeEvent changeEvent, + private static Result DeserializeChangeEvent(EventStreamChangeEvent changeEvent, IEventSourcedChangeEventMigrator migrator) { return changeEvent.Metadata.CreateEventFromJson(changeEvent.Id, changeEvent.Data, migrator); diff --git a/src/Infrastructure.Eventing.Common/Notifications/EventNotificationRegistration.cs b/src/Infrastructure.Eventing.Common/Notifications/EventNotificationRegistration.cs index d333201b..cf9047c3 100644 --- a/src/Infrastructure.Eventing.Common/Notifications/EventNotificationRegistration.cs +++ b/src/Infrastructure.Eventing.Common/Notifications/EventNotificationRegistration.cs @@ -3,18 +3,18 @@ namespace Infrastructure.Eventing.Common.Notifications; /// -/// Provides the registration information for both a producer and consumer +/// Provides the registration information for both consumers of domain and integration events /// public sealed class EventNotificationRegistration : IEventNotificationRegistration { - public EventNotificationRegistration(IEventNotificationProducer producer, - IEventNotificationConsumer consumer) + public EventNotificationRegistration(IIntegrationEventNotificationTranslator translator, + List domainEventConsumers) { - Producer = producer; - Consumer = consumer; + DomainEventConsumers = domainEventConsumers; + IntegrationEventTranslator = translator; } - public IEventNotificationProducer Producer { get; } + public IIntegrationEventNotificationTranslator IntegrationEventTranslator { get; } - public IEventNotificationConsumer Consumer { get; } + public List DomainEventConsumers { get; } } \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/IntegrationEvent.cs b/src/Infrastructure.Eventing.Common/Notifications/IntegrationEvent.cs new file mode 100644 index 00000000..e05dccaf --- /dev/null +++ b/src/Infrastructure.Eventing.Common/Notifications/IntegrationEvent.cs @@ -0,0 +1,25 @@ +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace Infrastructure.Eventing.Common.Notifications; + +/// +/// Defines a base class for integration events +/// +public class IntegrationEvent : IIntegrationEvent +{ + protected IntegrationEvent() + { + RootId = null!; + OccurredUtc = DateTime.UtcNow; + } + + protected IntegrationEvent(string rootId) + { + RootId = rootId; + OccurredUtc = DateTime.UtcNow; + } + + public string RootId { get; set; } + + public DateTime OccurredUtc { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/NoOpConsumerRegistration.cs b/src/Infrastructure.Eventing.Common/Notifications/NoOpConsumerRegistration.cs deleted file mode 100644 index c7b30c56..00000000 --- a/src/Infrastructure.Eventing.Common/Notifications/NoOpConsumerRegistration.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Domain.Interfaces.Entities; -using Infrastructure.Eventing.Interfaces.Notifications; - -namespace Infrastructure.Eventing.Common.Notifications; - -/// -/// Provides a registration for a consumer that handles all events but does nothing with them -/// -public sealed class NoOpConsumerRegistration : IEventNotificationRegistration - where TAggregateRoot : IEventingAggregateRoot -{ - public IEventNotificationProducer Producer => new PassThroughEventNotificationProducer(); - - public IEventNotificationConsumer Consumer => new NoOpConsumer(); -} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/NoOpConsumer.cs b/src/Infrastructure.Eventing.Common/Notifications/NoOpDomainEventConsumer.cs similarity index 55% rename from src/Infrastructure.Eventing.Common/Notifications/NoOpConsumer.cs rename to src/Infrastructure.Eventing.Common/Notifications/NoOpDomainEventConsumer.cs index 58634e35..5a290fc2 100644 --- a/src/Infrastructure.Eventing.Common/Notifications/NoOpConsumer.cs +++ b/src/Infrastructure.Eventing.Common/Notifications/NoOpDomainEventConsumer.cs @@ -7,10 +7,10 @@ namespace Infrastructure.Eventing.Common.Notifications; /// /// Provides a consumer that handles all events and does nothing with them /// -public sealed class NoOpConsumer : IEventNotificationConsumer +public sealed class NoOpDomainEventConsumer : IDomainEventNotificationConsumer { - public Task> NotifyAsync(IDomainEvent changeEvent, CancellationToken cancellationToken) + public Task> NotifyAsync(IDomainEvent domainEvent, CancellationToken cancellationToken) { - return Task.FromResult>(true); + return Task.FromResult(Result.Ok); } } \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationMessageBroker.cs b/src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationMessageBroker.cs new file mode 100644 index 00000000..c87f119f --- /dev/null +++ b/src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationMessageBroker.cs @@ -0,0 +1,17 @@ +using Common; +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace Infrastructure.Eventing.Common.Notifications; + +/// +/// Provides an implementation of that does nothing. +/// +public class NoOpEventNotificationMessageBroker : IEventNotificationMessageBroker +{ + public async Task> PublishAsync(IIntegrationEvent integrationEvent, + CancellationToken cancellationToken) + { + await Task.CompletedTask; + return Result.Ok; + } +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationRegistration.cs b/src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationRegistration.cs new file mode 100644 index 00000000..5a523ac9 --- /dev/null +++ b/src/Infrastructure.Eventing.Common/Notifications/NoOpEventNotificationRegistration.cs @@ -0,0 +1,16 @@ +using Domain.Interfaces.Entities; +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace Infrastructure.Eventing.Common.Notifications; + +/// +/// Provides a registration that handles no events +/// +public sealed class NoOpEventNotificationRegistration : IEventNotificationRegistration + where TAggregateRoot : IEventingAggregateRoot +{ + public IIntegrationEventNotificationTranslator IntegrationEventTranslator => + new NoOpIntegrationEventNotificationTranslator(); + + public List DomainEventConsumers => [new NoOpDomainEventConsumer()]; +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/NoOpIntegrationEventNotificationTranslator.cs b/src/Infrastructure.Eventing.Common/Notifications/NoOpIntegrationEventNotificationTranslator.cs new file mode 100644 index 00000000..a9e90201 --- /dev/null +++ b/src/Infrastructure.Eventing.Common/Notifications/NoOpIntegrationEventNotificationTranslator.cs @@ -0,0 +1,20 @@ +using Common; +using Domain.Interfaces.Entities; +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace Infrastructure.Eventing.Common.Notifications; + +/// +/// Provides a translator of domain events that never returns an integration event +/// +public sealed class NoOpIntegrationEventNotificationTranslator : IIntegrationEventNotificationTranslator + where TAggregateRoot : IEventingAggregateRoot +{ + public Type RootAggregateType => typeof(TAggregateRoot); + + public Task, Error>> TranslateAsync(IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + return Task.FromResult, Error>>(Optional.None); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Notifications/PassThroughEventNotificationProducer.cs b/src/Infrastructure.Eventing.Common/Notifications/PassThroughEventNotificationProducer.cs deleted file mode 100644 index 790e9979..00000000 --- a/src/Infrastructure.Eventing.Common/Notifications/PassThroughEventNotificationProducer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Common; -using Domain.Interfaces.Entities; -using Infrastructure.Eventing.Interfaces.Notifications; - -namespace Infrastructure.Eventing.Common.Notifications; - -/// -/// Provides a producer of notification events that simply passes on the published event -/// -public sealed class PassThroughEventNotificationProducer : IEventNotificationProducer - where TAggregateRoot : IEventingAggregateRoot -{ - public Type RootAggregateType => typeof(TAggregateRoot); - - public Task, Error>> PublishAsync(IDomainEvent changeEvent, - CancellationToken cancellationToken) - { - return Task.FromResult, Error>>(new Optional(changeEvent)); - } -} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Common/Projections/ReadModelProjector.cs b/src/Infrastructure.Eventing.Common/Projections/ReadModelProjector.cs index fd8c01ec..08cf7bc6 100644 --- a/src/Infrastructure.Eventing.Common/Projections/ReadModelProjector.cs +++ b/src/Infrastructure.Eventing.Common/Projections/ReadModelProjector.cs @@ -54,7 +54,7 @@ public async Task> WriteEventStreamAsync(string streamName, List - /// Looks up a localized string similar to The consumer '{0}' did not handle the event '{1}' with event type '{2}'. Aborting notifications. - /// - internal static string EventNotificationNotifier_ConsumerError { - get { - return ResourceManager.GetString("EventNotificationNotifier_ConsumerError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The producer '{0}' did not handle the event '{1}' with event type '{2}'. Aborting notifications. + /// Looks up a localized string similar to The producer '{0}' failed to handle the domain event '{1}' with event type '{2}'. Aborting notifications. /// internal static string EventNotificationNotifier_ProducerError { get { diff --git a/src/Infrastructure.Eventing.Common/Resources.resx b/src/Infrastructure.Eventing.Common/Resources.resx index 9f513598..2976d494 100644 --- a/src/Infrastructure.Eventing.Common/Resources.resx +++ b/src/Infrastructure.Eventing.Common/Resources.resx @@ -37,9 +37,6 @@ The event stream {0} is at checkpoint '{1}', but new events are at version {2}. Perhaps some event history is missing? - The producer '{0}' did not handle the event '{1}' with event type '{2}'. Aborting notifications - - - The consumer '{0}' did not handle the event '{1}' with event type '{2}'. Aborting notifications + The producer '{0}' failed to handle the domain event '{1}' with event type '{2}'. Aborting notifications \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IDomainEventNotificationConsumer.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IDomainEventNotificationConsumer.cs new file mode 100644 index 00000000..0a701b00 --- /dev/null +++ b/src/Infrastructure.Eventing.Interfaces/Notifications/IDomainEventNotificationConsumer.cs @@ -0,0 +1,15 @@ +using Common; +using Domain.Interfaces.Entities; + +namespace Infrastructure.Eventing.Interfaces.Notifications; + +/// +/// Defines a consumer of domain events +/// +public interface IDomainEventNotificationConsumer +{ + /// + /// Handles the notification of a + /// + Task> NotifyAsync(IDomainEvent domainEvent, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationConsumer.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationConsumer.cs deleted file mode 100644 index 78ce17e0..00000000 --- a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationConsumer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Common; -using Domain.Interfaces.Entities; - -namespace Infrastructure.Eventing.Interfaces.Notifications; - -/// -/// Defines a consumer of events -/// -public interface IEventNotificationConsumer -{ - /// - /// Handles the notification of the , and returns whether it was handled or not - /// - Task> NotifyAsync(IDomainEvent changeEvent, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationMessageBroker.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationMessageBroker.cs new file mode 100644 index 00000000..262e0978 --- /dev/null +++ b/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationMessageBroker.cs @@ -0,0 +1,14 @@ +using Common; + +namespace Infrastructure.Eventing.Interfaces.Notifications; + +/// +/// Defines a message broker for receiving and publishing integration events +/// +public interface IEventNotificationMessageBroker +{ + /// + /// Publishes the to some message broker + /// + Task> PublishAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationProducer.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationProducer.cs deleted file mode 100644 index 8ab1cf5f..00000000 --- a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationProducer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Common; -using Domain.Interfaces.Entities; - -namespace Infrastructure.Eventing.Interfaces.Notifications; - -/// -/// Defines a producer of events from a domain aggregate root -/// -public interface IEventNotificationProducer -{ - /// - /// Returns the type of the root aggregate that produces the events - /// - Type RootAggregateType { get; } - - /// - /// Handles the notification of a new , and returns the actual event to publish - /// to downstream consumers - /// - Task, Error>> PublishAsync(IDomainEvent changeEvent, - CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationRegistration.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationRegistration.cs index b8010255..79b314c8 100644 --- a/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationRegistration.cs +++ b/src/Infrastructure.Eventing.Interfaces/Notifications/IEventNotificationRegistration.cs @@ -1,17 +1,17 @@ namespace Infrastructure.Eventing.Interfaces.Notifications; /// -/// Defines the registration information for both a notifications producer and a notifications consumer +/// Defines the registration information for both a domain and integration consumers /// public interface IEventNotificationRegistration { /// - /// Returns the consumer of the events + /// Returns the consumers of domain events /// - IEventNotificationConsumer Consumer { get; } + List DomainEventConsumers { get; } /// - /// Returns the producer of the events + /// Returns the translator of integration events /// - IEventNotificationProducer Producer { get; } + IIntegrationEventNotificationTranslator IntegrationEventTranslator { get; } } \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEvent.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEvent.cs new file mode 100644 index 00000000..0b63ec0e --- /dev/null +++ b/src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEvent.cs @@ -0,0 +1,17 @@ +namespace Infrastructure.Eventing.Interfaces.Notifications; + +/// +/// Defines an integration event to communicate past events of an aggregate outside the process +/// +public interface IIntegrationEvent +{ + /// + /// Returns the time when the event happened + /// + DateTime OccurredUtc { get; set; } + + /// + /// Returns the ID of the root aggregate + /// + string RootId { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEventNotificationTranslator.cs b/src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEventNotificationTranslator.cs new file mode 100644 index 00000000..e26a429d --- /dev/null +++ b/src/Infrastructure.Eventing.Interfaces/Notifications/IIntegrationEventNotificationTranslator.cs @@ -0,0 +1,22 @@ +using Common; +using Domain.Interfaces.Entities; + +namespace Infrastructure.Eventing.Interfaces.Notifications; + +/// +/// Defines a translator of domain events from a domain aggregate root to integration events +/// +public interface IIntegrationEventNotificationTranslator +{ + /// + /// Returns the type of the root aggregate that produces the domain events + /// + Type RootAggregateType { get; } + + /// + /// Handles the notification of a new , and returns an optional + /// event to be published to downstream consumers of integration events + /// + Task, Error>> TranslateAsync(IDomainEvent domainEvent, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs index d1567722..346abfeb 100644 --- a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/EventHandlerBaseSpec.cs @@ -63,7 +63,7 @@ public void WhenEventStreamChangedEventRaisedAndFromDifferentStreams_ThenWritesB Version = 5, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default }, @@ -74,7 +74,7 @@ public void WhenEventStreamChangedEventRaisedAndFromDifferentStreams_ThenWritesB Version = 3, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default }, @@ -85,7 +85,7 @@ public void WhenEventStreamChangedEventRaisedAndFromDifferentStreams_ThenWritesB Version = 4, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default } @@ -122,7 +122,7 @@ public void WhenEventStreamChangedEventRaisedAndFromDifferentStreamsAndWriteFail Version = 5, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default }, @@ -133,7 +133,7 @@ public void WhenEventStreamChangedEventRaisedAndFromDifferentStreamsAndWriteFail Version = 3, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default }, @@ -144,7 +144,7 @@ public void WhenEventStreamChangedEventRaisedAndFromDifferentStreamsAndWriteFail Version = 4, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default } @@ -179,7 +179,7 @@ public void WhenEventStreamChangedEventRaisedAndEventsAreOutOfOrder_ThenReturnsE Version = 5, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default }, @@ -190,7 +190,7 @@ public void WhenEventStreamChangedEventRaisedAndEventsAreOutOfOrder_ThenReturnsE Version = 2, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default }, @@ -201,7 +201,7 @@ public void WhenEventStreamChangedEventRaisedAndEventsAreOutOfOrder_ThenReturnsE Version = 4, Data = null!, Metadata = null!, - EntityType = null!, + RootAggregateType = null!, EventType = null!, LastPersistedAtUtc = default } diff --git a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs index bf53ebbc..3f201267 100644 --- a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelaySpec.cs @@ -16,8 +16,9 @@ namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.N [Trait("Category", "Unit")] public class InProcessSynchronousNotificationRelaySpec { - private readonly TestConsumer _consumer; + private readonly TestDomainConsumer _domainConsumer; private readonly EventSourcingDddCommandStore _eventSourcingStore; + private readonly TestMessageBroker _messageBroker; private readonly InProcessSynchronousNotificationRelay _relay; public InProcessSynchronousNotificationRelaySpec() @@ -37,16 +38,18 @@ public InProcessSynchronousNotificationRelaySpec() _eventSourcingStore = new EventSourcingDddCommandStore(recorder.Object, domainFactory.Object, migrator.Object, store.Object); - _consumer = new TestConsumer(); - var registration = new EventNotificationRegistration( - new PassThroughEventNotificationProducer(), - _consumer); + _domainConsumer = new TestDomainConsumer(); + _messageBroker = new TestMessageBroker(); + var registration = + new EventNotificationRegistration( + new NoOpIntegrationEventNotificationTranslator(), [_domainConsumer]); var registrations = new List { registration }; - _relay = new InProcessSynchronousNotificationRelay(recorder.Object, migrator.Object, registrations, + _relay = new InProcessSynchronousNotificationRelay(recorder.Object, migrator.Object, _messageBroker, + registrations, _eventSourcingStore); } @@ -73,8 +76,9 @@ public async Task WhenEventHandlerFired_ThenNotifierNotifies() await _eventSourcingStore.SaveAsync(aggregate, CancellationToken.None); - _consumer.ProjectedEvents.Length.Should().Be(2); - _consumer.ProjectedEvents[0].As().Id.Should().Be("aneventid1"); - _consumer.ProjectedEvents[1].As().Id.Should().Be("aneventid2"); + _domainConsumer.ProjectedEvents.Length.Should().Be(2); + _domainConsumer.ProjectedEvents[0].As().Id.Should().Be("aneventid1"); + _domainConsumer.ProjectedEvents[1].As().Id.Should().Be("aneventid2"); + _messageBroker.ProjectedEvents.Length.Should().Be(0); } } \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestDomainConsumer.cs similarity index 56% rename from src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs rename to src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestDomainConsumer.cs index e4abedf0..38a511e4 100644 --- a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestConsumer.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestDomainConsumer.cs @@ -4,16 +4,16 @@ namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.Notifications; -internal class TestConsumer : IEventNotificationConsumer +internal class TestDomainConsumer : IDomainEventNotificationConsumer { private readonly List _projectedEvents = new(); public IDomainEvent[] ProjectedEvents => _projectedEvents.ToArray(); - public Task> NotifyAsync(IDomainEvent changeEvent, CancellationToken cancellationToken) + public Task> NotifyAsync(IDomainEvent domainEvent, CancellationToken cancellationToken) { - _projectedEvents.Add(changeEvent); + _projectedEvents.Add(domainEvent); - return Task.FromResult>(true); + return Task.FromResult(Result.Ok); } } \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestMessageBroker.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestMessageBroker.cs new file mode 100644 index 00000000..e45bed6f --- /dev/null +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/Notifications/TestMessageBroker.cs @@ -0,0 +1,18 @@ +using Common; +using Infrastructure.Eventing.Interfaces.Notifications; + +namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing.Notifications; + +internal class TestMessageBroker : IEventNotificationMessageBroker +{ + private readonly List _projectedEvents = new(); + + public IIntegrationEvent[] ProjectedEvents => _projectedEvents.ToArray(); + + public Task> PublishAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken) + { + _projectedEvents.Add(integrationEvent); + + return Task.FromResult(Result.Ok); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs index 55c62639..f1ef6096 100644 --- a/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs +++ b/src/Infrastructure.Hosting.Common.UnitTests/ApplicationServices/Eventing/TestEvent.cs @@ -1,12 +1,12 @@ -using Domain.Interfaces.Entities; +using Domain.Common; namespace Infrastructure.Hosting.Common.UnitTests.ApplicationServices.Eventing; -public class TestEvent : IDomainEvent +public class TestEvent : DomainEvent { - public required string Id { get; set; } - - public DateTime OccurredUtc { get; set; } + public TestEvent() : base("arootid") + { + } - public string RootId { get; set; } = "arootid"; + public required string Id { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj b/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj index 27c20564..97476956 100644 --- a/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj +++ b/src/Infrastructure.Hosting.Common.UnitTests/Infrastructure.Hosting.Common.UnitTests.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs index 510276ed..d35413f9 100644 --- a/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs +++ b/src/Infrastructure.Hosting.Common/ApplicationServices/Eventing/Notifications/InProcessSynchronousNotificationRelay.cs @@ -14,10 +14,11 @@ public class InProcessSynchronousNotificationRelay : EventStreamHandlerBase, IEventNotifyingStoreNotificationRelay { public InProcessSynchronousNotificationRelay(IRecorder recorder, IEventSourcedChangeEventMigrator migrator, + IEventNotificationMessageBroker messageBroker, IEnumerable registrations, params IEventNotifyingStore[] eventingStores) : base(recorder, eventingStores) { - Notifier = new EventNotificationNotifier(recorder, migrator, registrations.ToArray()); + Notifier = new EventNotificationNotifier(recorder, migrator, registrations.ToList(), messageBroker); } protected override void Dispose(bool disposing) diff --git a/src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs b/src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs index a1795d89..345caf64 100644 --- a/src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs +++ b/src/Infrastructure.Hosting.Common/Extensions/EventingExtensions.cs @@ -136,7 +136,7 @@ private static IServiceCollection AddEventing(services, scope, projectionFactory); Eventing.AddNotificationFactory(); - services.AddWithLifetime(scope, notificationFactory); + services.AddPerHttpRequest(notificationFactory); return services; } @@ -212,6 +212,7 @@ IDataStore DataStoreFactory(IServiceProvider c) new InProcessSynchronousNotificationRelay( c.GetRequiredService(), c.GetRequiredService(), + c.GetRequiredService(), Eventing.ResolveNotificationRegistrations(c), Eventing.ResolveNotificationStores(c).ToArray())); diff --git a/src/Infrastructure.Persistence.Common/Extensions/EventNotifyingStoreExtensions.cs b/src/Infrastructure.Persistence.Common/Extensions/EventNotifyingStoreExtensions.cs index 2430f12d..7ec12c1c 100644 --- a/src/Infrastructure.Persistence.Common/Extensions/EventNotifyingStoreExtensions.cs +++ b/src/Infrastructure.Persistence.Common/Extensions/EventNotifyingStoreExtensions.cs @@ -53,7 +53,7 @@ private static EventStreamChangeEvent ToChangeEvent(EventSourcedChangeEvent chan return new EventStreamChangeEvent { Data = changeEvent.Data, - EntityType = changeEvent.EntityType, + RootAggregateType = changeEvent.EntityType, EventType = changeEvent.EventType, Id = changeEvent.Id, LastPersistedAtUtc = changeEvent.LastPersistedAtUtc, diff --git a/src/Infrastructure.Shared/Eventing/Notifications/ExampleEventNotificationMessageBroker.cs b/src/Infrastructure.Shared/Eventing/Notifications/ExampleEventNotificationMessageBroker.cs new file mode 100644 index 00000000..dfd3b60c --- /dev/null +++ b/src/Infrastructure.Shared/Eventing/Notifications/ExampleEventNotificationMessageBroker.cs @@ -0,0 +1,40 @@ +using Application.Common.Extensions; +using Common; +using Infrastructure.Eventing.Interfaces.Notifications; +using Infrastructure.Interfaces; +using Integration.Events.Shared.EndUsers; + +namespace Infrastructure.Shared.Eventing.Notifications; + +/// +/// Provides an example message broker that relays integration events to external systems, +/// as eventually consistent with this process +/// +public class ExampleEventNotificationMessageBroker : IEventNotificationMessageBroker +{ + private readonly ICallerContextFactory _callerContextFactory; + private readonly IRecorder _recorder; + + public ExampleEventNotificationMessageBroker(IRecorder recorder, ICallerContextFactory callerContextFactory) + { + _recorder = recorder; + _callerContextFactory = callerContextFactory; + } + + public async Task> PublishAsync(IIntegrationEvent integrationEvent, + CancellationToken cancellationToken) + { + switch (integrationEvent) + { + case PersonRegistered registered: + await Task.CompletedTask; + _recorder.TraceDebug(_callerContextFactory.Create().ToCall(), + "User {Id} was registered with username {Username}", + registered.RootId, registered.Username); + return Result.Ok; + + default: + return Result.Ok; + } + } +} \ No newline at end of file diff --git a/src/Infrastructure.Shared/Infrastructure.Shared.csproj b/src/Infrastructure.Shared/Infrastructure.Shared.csproj index 55c70a62..4bdd521e 100644 --- a/src/Infrastructure.Shared/Infrastructure.Shared.csproj +++ b/src/Infrastructure.Shared/Infrastructure.Shared.csproj @@ -9,10 +9,12 @@ + + diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs b/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs index 296a3d02..dd3f99b5 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs +++ b/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs @@ -19,6 +19,7 @@ using Infrastructure.Common; using Infrastructure.Common.Extensions; using Infrastructure.Eventing.Common.Projections.ReadModels; +using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Hosting.Common; using Infrastructure.Hosting.Common.Extensions; using Infrastructure.Hosting.Common.Recording; @@ -28,6 +29,7 @@ using Infrastructure.Persistence.Shared.ApplicationServices; using Infrastructure.Shared.ApplicationServices; using Infrastructure.Shared.ApplicationServices.External; +using Infrastructure.Shared.Eventing.Notifications; using Infrastructure.Web.Api.Common; using Infrastructure.Web.Api.Common.Extensions; using Infrastructure.Web.Api.Common.Validation; @@ -61,7 +63,7 @@ public static class HostExtensions private const string AllowedCORSOriginsSettingName = "Hosts:AllowedCORSOrigins"; private const string CheckPointAggregatePrefix = "check"; private const string LoggingSettingName = "Logging"; - private static readonly char[] AllowedCORSOriginsDelimiters = { ',', ';', ' ' }; + private static readonly char[] AllowedCORSOriginsDelimiters = [',', ';', ' ']; #if TESTINGONLY private static readonly Dictionary StubQueueDrainingServiceQueuedApiMappings = new() { @@ -90,6 +92,7 @@ public static WebApplication ConfigureApiHost(this WebApplicationBuilder appBuil RegisterNotifications(hostOptions.UsesNotifications); modules.RegisterServices(appBuilder.Configuration, services); RegisterApplicationServices(hostOptions.IsMultiTenanted); + RegisterEventing(hostOptions.Persistence.UsesEventing); RegisterPersistence(hostOptions.Persistence.UsesQueues, hostOptions.IsMultiTenanted); RegisterCors(hostOptions.CORS); @@ -380,6 +383,14 @@ void RegisterApplicationServices(bool isMultiTenanted) } } + void RegisterEventing(bool usesEventing) + { + if (usesEventing) + { + services.AddPerHttpRequest(); + } + } + void RegisterPersistence(bool usesQueues, bool isMultiTenanted) { var domainAssemblies = modules.SubdomainAssemblies diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/WebApplicationExtensions.cs b/src/Infrastructure.Web.Hosting.Common/Extensions/WebApplicationExtensions.cs index 4c19a0ea..b6ceea63 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/WebApplicationExtensions.cs +++ b/src/Infrastructure.Web.Hosting.Common/Extensions/WebApplicationExtensions.cs @@ -157,7 +157,7 @@ public static void EnableEventingPropagation(this WebApplication builder, await next(); }); - }, "Pipeline: Event Projections/Notifications is enabled")); + }, "Pipeline: Event Projections/Notifications are enabled")); } /// diff --git a/src/Integration.Events.Shared/EndUsers/PersonRegistered.cs b/src/Integration.Events.Shared/EndUsers/PersonRegistered.cs new file mode 100644 index 00000000..347dec0f --- /dev/null +++ b/src/Integration.Events.Shared/EndUsers/PersonRegistered.cs @@ -0,0 +1,25 @@ +using Domain.Shared.EndUsers; +using Infrastructure.Eventing.Common.Notifications; +using JetBrains.Annotations; + +namespace Integration.Events.Shared.EndUsers; + +public sealed class PersonRegistered : IntegrationEvent +{ + public PersonRegistered(string id) : base(id) + { + } + + [UsedImplicitly] + public PersonRegistered() + { + } + + public required List Features { get; set; } + + public required List Roles { get; set; } + + public required string Username { get; set; } + + public required RegisteredUserProfile UserProfile { get; set; } +} \ No newline at end of file diff --git a/src/Integration.Events.Shared/Integration.Events.Shared.csproj b/src/Integration.Events.Shared/Integration.Events.Shared.csproj new file mode 100644 index 00000000..e51a3ef8 --- /dev/null +++ b/src/Integration.Events.Shared/Integration.Events.Shared.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + + + + + + + + + + <_Parameter1>$(AssemblyName).UnitTests + + + + diff --git a/src/OrganizationsApplication.UnitTests/OrganizationsApplication.DomainEventHandlersSpec.cs b/src/OrganizationsApplication.UnitTests/OrganizationsApplication.DomainEventHandlersSpec.cs new file mode 100644 index 00000000..5167533d --- /dev/null +++ b/src/OrganizationsApplication.UnitTests/OrganizationsApplication.DomainEventHandlersSpec.cs @@ -0,0 +1,105 @@ +using Application.Interfaces; +using Application.Interfaces.Services; +using Application.Services.Shared; +using Common; +using Domain.Common.Identity; +using Domain.Common.ValueObjects; +using Domain.Interfaces.Entities; +using Domain.Interfaces.Services; +using Domain.Shared; +using Domain.Shared.EndUsers; +using EndUsersDomain; +using FluentAssertions; +using Moq; +using OrganizationsApplication.Persistence; +using OrganizationsDomain; +using UnitTesting.Common; +using Xunit; +using Events = EndUsersDomain.Events; +using OrganizationOwnership = Domain.Shared.Organizations.OrganizationOwnership; + +namespace OrganizationsApplication.UnitTests; + +[Trait("Category", "Unit")] +public class OrganizationsApplicationDomainEventHandlersSpec +{ + private readonly OrganizationsApplication _application; + private readonly Mock _caller; + private readonly Mock _repository; + private readonly Mock _tenantSettingsService; + + public OrganizationsApplicationDomainEventHandlersSpec() + { + var recorder = new Mock(); + _caller = new Mock(); + var idFactory = new Mock(); + idFactory.Setup(f => f.Create(It.IsAny())) + .Returns("anid".ToId()); + _tenantSettingsService = new Mock(); + _tenantSettingsService.Setup(tss => + tss.CreateForTenantAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new TenantSettings(new Dictionary + { + { "aname", "avalue" } + })); + var tenantSettingService = new Mock(); + tenantSettingService.Setup(tss => tss.Encrypt(It.IsAny())) + .Returns((string value) => value); + tenantSettingService.Setup(tss => tss.Decrypt(It.IsAny())) + .Returns((string value) => value); + var endUsersService = new Mock(); + _repository = new Mock(); + _repository.Setup(ar => ar.SaveAsync(It.IsAny(), It.IsAny())) + .Returns((OrganizationRoot root, CancellationToken _) => + Task.FromResult>(root)); + + _application = new OrganizationsApplication(recorder.Object, idFactory.Object, + _tenantSettingsService.Object, tenantSettingService.Object, endUsersService.Object, _repository.Object); + } + + [Fact] + public async Task WhenHandleEndUserRegisteredForPersonAsync_ThenReturnsOrganization() + { + var domainEvent = Events.Registered("auserid".ToId(), EndUserProfile.Create("afirstname", "alastname").Value, + EmailAddress.Create("auser@company.com").Value, UserClassification.Person, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = + await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeSuccess(); + _repository.Verify(rep => rep.SaveAsync(It.Is(org => + org.Name == "afirstname alastname" + && org.Ownership == OrganizationOwnership.Personal + && org.CreatedById == "auserid" + && org.Settings.Properties.Count == 1 + && org.Settings.Properties["aname"].Value.As() == "avalue" + && org.Settings.Properties["aname"].IsEncrypted == false + ), It.IsAny())); + _tenantSettingsService.Verify(tss => + tss.CreateForTenantAsync(_caller.Object, "anid", It.IsAny())); + } + + [Fact] + public async Task WhenHandleEndUserRegisteredForMachineAsync_ThenReturnsOrganization() + { + var domainEvent = Events.Registered("auserid".ToId(), EndUserProfile.Create("amachinename").Value, + EmailAddress.Create("auser@company.com").Value, UserClassification.Machine, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = + await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeSuccess(); + _repository.Verify(rep => rep.SaveAsync(It.Is(org => + org.Name == "amachinename" + && org.Ownership == OrganizationOwnership.Personal + && org.CreatedById == "auserid" + && org.Settings.Properties.Count == 1 + && org.Settings.Properties["aname"].Value.As() == "avalue" + && org.Settings.Properties["aname"].IsEncrypted == false + ), It.IsAny())); + _tenantSettingsService.Verify(tss => + tss.CreateForTenantAsync(_caller.Object, "anid", It.IsAny())); + } +} \ No newline at end of file diff --git a/src/OrganizationsApplication.UnitTests/OrganizationsApplication.UnitTests.csproj b/src/OrganizationsApplication.UnitTests/OrganizationsApplication.UnitTests.csproj index dca39015..e37be158 100644 --- a/src/OrganizationsApplication.UnitTests/OrganizationsApplication.UnitTests.csproj +++ b/src/OrganizationsApplication.UnitTests/OrganizationsApplication.UnitTests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs b/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs index d9ecb616..817eb7fe 100644 --- a/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs +++ b/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs @@ -59,43 +59,11 @@ public OrganizationsApplicationSpec() _tenantSettingsService.Object, _tenantSettingService.Object, _endUsersService.Object, _repository.Object); } - [Fact] - public async Task WhenCreateOrganizationAsync_ThenReturnsOrganization() - { - var result = - await _application.CreateOrganizationAsync(_caller.Object, "auserid", "aname", - Application.Resources.Shared.OrganizationOwnership.Personal, CancellationToken.None); - - result.Value.Name.Should().Be("aname"); - result.Value.Ownership.Should().Be(Application.Resources.Shared.OrganizationOwnership.Personal); - result.Value.CreatedById.Should().Be("auserid"); - _repository.Verify(rep => rep.SaveAsync(It.Is(org => - org.Name == "aname" - && org.Ownership == OrganizationOwnership.Personal - && org.CreatedById == "auserid" - && org.Settings.Properties.Count == 1 - && org.Settings.Properties["aname"].Value.As() == "avalue" - && org.Settings.Properties["aname"].IsEncrypted == false - ), It.IsAny())); - _tenantSettingsService.Verify(tss => - tss.CreateForTenantAsync(_caller.Object, "anid", It.IsAny())); - } - [Fact] public async Task WhenCreateSharedOrganizationAsync_ThenReturnsSharedOrganization() { _caller.Setup(c => c.CallerId) .Returns("acallerid"); - _endUsersService.Setup(eus => - eus.CreateMembershipForCallerPrivateAsync(It.IsAny(), It.IsAny(), - It.IsAny())) - .ReturnsAsync(new Membership - { - Id = "amembershipid", - UserId = "auserid", - OrganizationId = "anorganizationid", - IsDefault = false - }); var result = await _application.CreateSharedOrganizationAsync(_caller.Object, "aname", @@ -114,8 +82,6 @@ await _application.CreateSharedOrganizationAsync(_caller.Object, "aname", ), It.IsAny())); _tenantSettingsService.Verify(tss => tss.CreateForTenantAsync(_caller.Object, "anid", It.IsAny())); - _endUsersService.Verify(eus => - eus.CreateMembershipForCallerPrivateAsync(_caller.Object, "anid", It.IsAny())); } [Fact] diff --git a/src/OrganizationsApplication/IOrganizationsApplication.DomainEventHandlers.cs b/src/OrganizationsApplication/IOrganizationsApplication.DomainEventHandlers.cs new file mode 100644 index 00000000..3e7f79ba --- /dev/null +++ b/src/OrganizationsApplication/IOrganizationsApplication.DomainEventHandlers.cs @@ -0,0 +1,11 @@ +using Application.Interfaces; +using Common; +using Domain.Events.Shared.EndUsers; + +namespace OrganizationsApplication; + +partial interface IOrganizationsApplication +{ + Task> HandleEndUserRegisteredAsync(ICallerContext caller, Registered domainEvent, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/OrganizationsApplication/IOrganizationsApplication.cs b/src/OrganizationsApplication/IOrganizationsApplication.cs index 382f0eb5..0d3556c5 100644 --- a/src/OrganizationsApplication/IOrganizationsApplication.cs +++ b/src/OrganizationsApplication/IOrganizationsApplication.cs @@ -5,14 +5,11 @@ namespace OrganizationsApplication; -public interface IOrganizationsApplication +public partial interface IOrganizationsApplication { Task> ChangeSettingsAsync(ICallerContext caller, string id, TenantSettings settings, CancellationToken cancellationToken); - Task> CreateOrganizationAsync(ICallerContext caller, string creatorId, string name, - OrganizationOwnership ownership, CancellationToken cancellationToken); - Task> CreateSharedOrganizationAsync(ICallerContext caller, string name, CancellationToken cancellationToken); diff --git a/src/OrganizationsApplication/OrganizationsApplication.DomainEventHandlers.cs b/src/OrganizationsApplication/OrganizationsApplication.DomainEventHandlers.cs new file mode 100644 index 00000000..4b04d4d4 --- /dev/null +++ b/src/OrganizationsApplication/OrganizationsApplication.DomainEventHandlers.cs @@ -0,0 +1,26 @@ +using Application.Interfaces; +using Application.Resources.Shared; +using Common; +using Common.Extensions; +using Domain.Common.ValueObjects; +using Domain.Events.Shared.EndUsers; + +namespace OrganizationsApplication; + +partial class OrganizationsApplication +{ + public async Task> HandleEndUserRegisteredAsync(ICallerContext caller, Registered domainEvent, + CancellationToken cancellationToken) + { + var name = + $"{domainEvent.UserProfile.FirstName}{(domainEvent.UserProfile.LastName.HasValue() ? " " + domainEvent.UserProfile.LastName : string.Empty)}"; + var organization = await CreateOrganizationAsync(caller, domainEvent.RootId.ToId(), name, + OrganizationOwnership.Personal, cancellationToken); + if (!organization.IsSuccessful) + { + return organization.Error; + } + + return Result.Ok; + } +} \ No newline at end of file diff --git a/src/OrganizationsApplication/OrganizationsApplication.cs b/src/OrganizationsApplication/OrganizationsApplication.cs index a2f66c33..630ac469 100644 --- a/src/OrganizationsApplication/OrganizationsApplication.cs +++ b/src/OrganizationsApplication/OrganizationsApplication.cs @@ -16,7 +16,7 @@ namespace OrganizationsApplication; -public class OrganizationsApplication : IOrganizationsApplication +public partial class OrganizationsApplication : IOrganizationsApplication { private readonly IEndUsersService _endUsersService; private readonly IIdentifierFactory _identifierFactory; @@ -71,55 +71,6 @@ public async Task> ChangeSettingsAsync(ICallerContext caller, stri return Result.Ok; } - public async Task> CreateOrganizationAsync(ICallerContext caller, string creatorId, - string name, Application.Resources.Shared.OrganizationOwnership ownership, CancellationToken cancellationToken) - { - var displayName = DisplayName.Create(name); - if (!displayName.IsSuccessful) - { - return displayName.Error; - } - - var created = OrganizationRoot.Create(_recorder, _identifierFactory, _tenantSettingService, - ownership.ToEnumOrDefault(OrganizationOwnership.Shared), creatorId.ToId(), displayName.Value); - if (!created.IsSuccessful) - { - return created.Error; - } - - var org = created.Value; - var newSettings = await _tenantSettingsService.CreateForTenantAsync(caller, org.Id, cancellationToken); - if (!newSettings.IsSuccessful) - { - return newSettings.Error; - } - - var organizationSettings = newSettings.Value.ToSettings(); - if (!organizationSettings.IsSuccessful) - { - return organizationSettings.Error; - } - - var configured = org.CreateSettings(organizationSettings.Value); - if (!configured.IsSuccessful) - { - return configured.Error; - } - - //TODO: Get the billing details for the creator and add the billing subscription for them - - var saved = await _repository.SaveAsync(org, cancellationToken); - if (!saved.IsSuccessful) - { - return saved.Error; - } - - _recorder.TraceInformation(caller.ToCall(), "Created organization: {Id}, by {CreatedBy}", org.Id, - saved.Value.CreatedById); - - return saved.Value.ToOrganization(); - } - public async Task> CreateSharedOrganizationAsync(ICallerContext caller, string name, CancellationToken cancellationToken) { @@ -131,16 +82,7 @@ public async Task> CreateSharedOrganizationAsync(ICa return created.Error; } - //TODO: replaced by a notification! - var organization = created.Value; - var membership = - await _endUsersService.CreateMembershipForCallerPrivateAsync(caller, organization.Id, cancellationToken); - if (!membership.IsSuccessful) - { - return membership.Error; - } - - return organization; + return created.Value; } public async Task> GetOrganizationAsync(ICallerContext caller, string id, @@ -256,6 +198,55 @@ await _endUsersService.ListMembershipsForOrganizationAsync(caller, organization. return searchOptions.ApplyWithMetadata(memberships.Value.Results.ConvertAll(x => x.ToMember())); } + + private async Task> CreateOrganizationAsync(ICallerContext caller, string creatorId, + string name, Application.Resources.Shared.OrganizationOwnership ownership, CancellationToken cancellationToken) + { + var displayName = DisplayName.Create(name); + if (!displayName.IsSuccessful) + { + return displayName.Error; + } + + var created = OrganizationRoot.Create(_recorder, _identifierFactory, _tenantSettingService, + ownership.ToEnumOrDefault(OrganizationOwnership.Shared), creatorId.ToId(), displayName.Value); + if (!created.IsSuccessful) + { + return created.Error; + } + + var org = created.Value; + var newSettings = await _tenantSettingsService.CreateForTenantAsync(caller, org.Id, cancellationToken); + if (!newSettings.IsSuccessful) + { + return newSettings.Error; + } + + var organizationSettings = newSettings.Value.ToSettings(); + if (!organizationSettings.IsSuccessful) + { + return organizationSettings.Error; + } + + var configured = org.CreateSettings(organizationSettings.Value); + if (!configured.IsSuccessful) + { + return configured.Error; + } + + //TODO: Get the billing details for the creator and add the billing subscription for them + + var saved = await _repository.SaveAsync(org, cancellationToken); + if (!saved.IsSuccessful) + { + return saved.Error; + } + + _recorder.TraceInformation(caller.ToCall(), "Created organization: {Id}, by {CreatedBy}", org.Id, + saved.Value.CreatedById); + + return saved.Value.ToOrganization(); + } } internal static class OrganizationConversionExtensions diff --git a/src/OrganizationsDomain/Events.cs b/src/OrganizationsDomain/Events.cs index a7c7a776..ce9b7ff8 100644 --- a/src/OrganizationsDomain/Events.cs +++ b/src/OrganizationsDomain/Events.cs @@ -9,43 +9,37 @@ public static class Events public static Created Created(Identifier id, OrganizationOwnership ownership, Identifier createdBy, DisplayName name) { - return new Created + return new Created(id) { Name = name, Ownership = ownership, CreatedById = createdBy, - RootId = id, - OccurredUtc = DateTime.UtcNow }; } public static SettingCreated SettingCreated(Identifier id, string name, string value, SettingValueType valueType, bool isEncrypted) { - return new SettingCreated + return new SettingCreated(id) { - RootId = id, Name = name, StringValue = value, ValueType = valueType, - IsEncrypted = isEncrypted, - OccurredUtc = DateTime.UtcNow + IsEncrypted = isEncrypted }; } public static SettingUpdated SettingUpdated(Identifier id, string name, string from, SettingValueType fromType, string to, SettingValueType toType, bool isEncrypted) { - return new SettingUpdated + return new SettingUpdated(id) { - RootId = id, Name = name, From = from, FromType = fromType, To = to, ToType = toType, - IsEncrypted = isEncrypted, - OccurredUtc = DateTime.UtcNow + IsEncrypted = isEncrypted }; } } \ No newline at end of file diff --git a/src/OrganizationsDomain/OrganizationRoot.cs b/src/OrganizationsDomain/OrganizationRoot.cs index 7c741d05..b08e0ec9 100644 --- a/src/OrganizationsDomain/OrganizationRoot.cs +++ b/src/OrganizationsDomain/OrganizationRoot.cs @@ -66,8 +66,6 @@ public override Result EnsureInvariants() return ensureInvariants.Error; } - //TODO: add your other invariant rules here - return Result.Ok; } diff --git a/src/OrganizationsInfrastructure/ApplicationServices/OrganizationsInProcessServiceClient.cs b/src/OrganizationsInfrastructure/ApplicationServices/OrganizationsInProcessServiceClient.cs index 23bb619e..e448a68a 100644 --- a/src/OrganizationsInfrastructure/ApplicationServices/OrganizationsInProcessServiceClient.cs +++ b/src/OrganizationsInfrastructure/ApplicationServices/OrganizationsInProcessServiceClient.cs @@ -1,6 +1,5 @@ using Application.Interfaces; using Application.Interfaces.Services; -using Application.Resources.Shared; using Application.Services.Shared; using Common; using Common.Extensions; @@ -31,13 +30,6 @@ public async Task> ChangeSettingsPrivateAsync(ICallerContext calle return await GetApplication().ChangeSettingsAsync(caller, id, settings, cancellationToken); } - public async Task> CreateOrganizationPrivateAsync(ICallerContext caller, - string creatorId, string name, OrganizationOwnership ownership, CancellationToken cancellationToken) - { - return await GetApplication().CreateOrganizationAsync(caller, creatorId, name, ownership, - cancellationToken); - } - public async Task> GetSettingsPrivateAsync(ICallerContext caller, string id, CancellationToken cancellationToken) { diff --git a/src/OrganizationsInfrastructure/Notifications/OrganizationNotificationConsumer.cs b/src/OrganizationsInfrastructure/Notifications/OrganizationNotificationConsumer.cs new file mode 100644 index 00000000..242f4edb --- /dev/null +++ b/src/OrganizationsInfrastructure/Notifications/OrganizationNotificationConsumer.cs @@ -0,0 +1,34 @@ +using Common; +using Domain.Events.Shared.EndUsers; +using Domain.Interfaces.Entities; +using Infrastructure.Eventing.Interfaces.Notifications; +using Infrastructure.Interfaces; +using OrganizationsApplication; + +namespace OrganizationsInfrastructure.Notifications; + +public class OrganizationNotificationConsumer : IDomainEventNotificationConsumer +{ + private readonly ICallerContextFactory _callerContextFactory; + private readonly IOrganizationsApplication _organizationsApplication; + + public OrganizationNotificationConsumer(ICallerContextFactory callerContextFactory, + IOrganizationsApplication organizationsApplication) + { + _callerContextFactory = callerContextFactory; + _organizationsApplication = organizationsApplication; + } + + public async Task> NotifyAsync(IDomainEvent domainEvent, CancellationToken cancellationToken) + { + switch (domainEvent) + { + case Registered registered: + return await _organizationsApplication.HandleEndUserRegisteredAsync(_callerContextFactory.Create(), + registered, cancellationToken); + + default: + return Result.Ok; + } + } +} \ No newline at end of file diff --git a/src/OrganizationsInfrastructure/Notifications/OrganizationNotifier.cs b/src/OrganizationsInfrastructure/Notifications/OrganizationNotifier.cs new file mode 100644 index 00000000..62f518b5 --- /dev/null +++ b/src/OrganizationsInfrastructure/Notifications/OrganizationNotifier.cs @@ -0,0 +1,18 @@ +using Infrastructure.Eventing.Common.Notifications; +using Infrastructure.Eventing.Interfaces.Notifications; +using OrganizationsDomain; + +namespace OrganizationsInfrastructure.Notifications; + +public class OrganizationNotifier : IEventNotificationRegistration +{ + public OrganizationNotifier(IEnumerable consumers) + { + DomainEventConsumers = consumers.ToList(); + } + + public List DomainEventConsumers { get; } + + public IIntegrationEventNotificationTranslator IntegrationEventTranslator => + new NoOpIntegrationEventNotificationTranslator(); +} \ No newline at end of file diff --git a/src/OrganizationsInfrastructure/OrganizationsModule.cs b/src/OrganizationsInfrastructure/OrganizationsModule.cs index f548b555..531c0d05 100644 --- a/src/OrganizationsInfrastructure/OrganizationsModule.cs +++ b/src/OrganizationsInfrastructure/OrganizationsModule.cs @@ -8,7 +8,9 @@ using Domain.Interfaces; using Domain.Interfaces.Services; using Infrastructure.Common.DomainServices; +using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Hosting.Common.Extensions; +using Infrastructure.Interfaces; using Infrastructure.Persistence.Interfaces; using Infrastructure.Web.Hosting.Common; using Infrastructure.Web.Hosting.Common.ApplicationServices; @@ -19,6 +21,7 @@ using OrganizationsApplication.Persistence; using OrganizationsDomain; using OrganizationsInfrastructure.ApplicationServices; +using OrganizationsInfrastructure.Notifications; using OrganizationsInfrastructure.Persistence; using OrganizationsInfrastructure.Persistence.ReadModels; @@ -63,10 +66,16 @@ public Action RegisterServices c.GetRequiredService(), c.GetRequiredService>(), c.GetRequiredServiceForPlatform())); - services.RegisterUnTenantedEventing( + services + .AddPerHttpRequest(c => + new OrganizationNotificationConsumer(c.GetRequiredService(), + c.GetRequiredService())); + services.RegisterUnTenantedEventing( c => new OrganizationProjection(c.GetRequiredService(), c.GetRequiredService(), - c.GetRequiredServiceForPlatform())); + c.GetRequiredServiceForPlatform()), + c => new OrganizationNotifier(c + .GetRequiredService>())); services.AddSingleton(c => new OrganizationsInProcessServiceClient(c.LazyGetRequiredService())); diff --git a/src/SaaStack.sln b/src/SaaStack.sln index ad7b9af7..815d9101 100644 --- a/src/SaaStack.sln +++ b/src/SaaStack.sln @@ -344,6 +344,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserProfilesInfrastructure. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain.Events.Shared", "Domain.Events.Shared\Domain.Events.Shared.csproj", "{3AC2CCAF-A248-4FCA-9C42-BD207E528D27}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Events.Shared", "Integration.Events.Shared\Integration.Events.Shared.csproj", "{99DC5CFB-1DF8-45E4-9EE8-49D44B637198}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1101,6 +1103,12 @@ Global {3AC2CCAF-A248-4FCA-9C42-BD207E528D27}.Release|Any CPU.Build.0 = Release|Any CPU {3AC2CCAF-A248-4FCA-9C42-BD207E528D27}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU {3AC2CCAF-A248-4FCA-9C42-BD207E528D27}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198}.Release|Any CPU.Build.0 = Release|Any CPU + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5C77A86-38AF-40E4-82FC-617E624B2754} = {508E7DA4-4DF2-4201-955D-CCF70C41AD05} @@ -1267,5 +1275,6 @@ Global {8CB11E5D-01DE-4FCF-98B1-4998E80D561D} = {153F22CE-3C45-4CF5-991A-01C866EC429F} {A9D1A686-DEBA-4EC6-93E2-290392F41B13} = {153F22CE-3C45-4CF5-991A-01C866EC429F} {3AC2CCAF-A248-4FCA-9C42-BD207E528D27} = {3782A767-2274-4F44-80C6-D6C6EEB9C9A5} + {99DC5CFB-1DF8-45E4-9EE8-49D44B637198} = {3782A767-2274-4F44-80C6-D6C6EEB9C9A5} EndGlobalSection EndGlobal diff --git a/src/SaaStack.sln.DotSettings b/src/SaaStack.sln.DotSettings index 91cc9b75..e9cba390 100644 --- a/src/SaaStack.sln.DotSettings +++ b/src/SaaStack.sln.DotSettings @@ -564,6 +564,26 @@ public sealed class $name$ : ValueObjectBase<$name$> public $datatype$ $param3$ { get; } }$END$$SELECTION$ + True + True + True + typeName() + -1 + 0 + True + True + 2.0 + InCSharpFile + jj + True + public $class$(Identifier id) : base(id) + { + } + + [UsedImplicitly] + public $class$() + { + } True True A new test class (xUnit) @@ -774,7 +794,7 @@ public sealed class $name$Root : AggregateRootBase True 2.0 InCSharpFile - event + domainevent True public sealed class $name$ : IDomainEvent { @@ -1107,6 +1127,7 @@ public void When$condition$_Then$outcome$() True True True + True True True True diff --git a/src/Tools.Analyzers.Common/AnalyzerConstants.cs b/src/Tools.Analyzers.Common/AnalyzerConstants.cs index 361a1030..84821739 100644 --- a/src/Tools.Analyzers.Common/AnalyzerConstants.cs +++ b/src/Tools.Analyzers.Common/AnalyzerConstants.cs @@ -27,6 +27,7 @@ public static class Categories { public const string Application = "SaaStackApplication"; public const string Ddd = "SaaStackDDD"; + public const string Eventing = "SaaStackEventing"; public const string Documentation = "SaaStackDocumentation"; public const string WebApi = "SaaStackWebApi"; public const string Host = "SaaStackHosts"; diff --git a/src/Tools.Analyzers.Common/Extensions/SymbolExtensions.cs b/src/Tools.Analyzers.Common/Extensions/SymbolExtensions.cs index 2d20b657..1f2386b4 100644 --- a/src/Tools.Analyzers.Common/Extensions/SymbolExtensions.cs +++ b/src/Tools.Analyzers.Common/Extensions/SymbolExtensions.cs @@ -32,6 +32,59 @@ public static string GetMethodBody(this ISymbol method) return string.Empty; } + public static bool HasParameterlessConstructor(this INamedTypeSymbol symbol) + { + var constructors = symbol.InstanceConstructors; + if (constructors.Length == 0) + { + return true; + } + + return symbol.InstanceConstructors + .Any(c => c.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public + && c.Parameters.Length == 0); + } + + public static bool HasPropertiesOfAllowableTypes(this INamedTypeSymbol symbol, + List allowableTypes) + { + var properties = symbol.GetMembers() + .OfType() + .Select(p => p.GetMethod?.ReturnType) + .Where(rt => rt is not null); + + foreach (var property in properties) + { + if (!allowableTypes.Any(allowableType => property!.IsOfType(allowableType))) + { + return false; + } + } + + return true; + } + + public static bool HasPublicGetterAndSetterProperties(this INamedTypeSymbol symbol) + { + var properties = symbol.GetMembers() + .OfType(); + + foreach (var property in properties) + { + if (property.DeclaredAccessibility != Microsoft.CodeAnalysis.Accessibility.Public) + { + return false; + } + + if (property.GetMethod is null || property.SetMethod is null) + { + return false; + } + } + + return true; + } + public static bool IsEnum(this ITypeSymbol symbol) { return symbol.TypeKind == TypeKind.Enum; diff --git a/src/Tools.Analyzers.Common/Extensions/SyntaxFilterExtensions.cs b/src/Tools.Analyzers.Common/Extensions/SyntaxFilterExtensions.cs index e6c9f9e1..2ae0dd54 100644 --- a/src/Tools.Analyzers.Common/Extensions/SyntaxFilterExtensions.cs +++ b/src/Tools.Analyzers.Common/Extensions/SyntaxFilterExtensions.cs @@ -56,6 +56,24 @@ public static class SyntaxFilterExtensions return null; } + public static bool HasParameterlessConstructor(this ClassDeclarationSyntax classDeclarationSyntax) + { + var allConstructors = classDeclarationSyntax.Members.Where(member => member is ConstructorDeclarationSyntax) + .Cast() + .ToList(); + if (allConstructors.Count > 0) + { + var parameterlessConstructors = allConstructors + .Where(constructor => constructor.ParameterList.Parameters.Count == 0 && constructor.IsPublic()); + if (!parameterlessConstructors.Any()) + { + return false; + } + } + + return true; + } + public static bool HasPublicGetterAndSetter(this PropertyDeclarationSyntax propertyDeclarationSyntax) { var propertyAccessibility = new Accessibility(propertyDeclarationSyntax.Modifiers); @@ -106,6 +124,25 @@ public static bool HasPublicGetterAndSetter(this PropertyDeclarationSyntax prope return true; } + public static bool HasPublicGetterAndSetterProperties(this ClassDeclarationSyntax classDeclarationSyntax) + { + var allProperties = classDeclarationSyntax.Members.Where(member => member is PropertyDeclarationSyntax) + .Cast() + .ToList(); + if (allProperties.Count > 0) + { + foreach (var property in allProperties) + { + if (!property.HasPublicGetterAndSetter()) + { + return false; + } + } + } + + return true; + } + public static bool HasPublicSetter(this PropertyDeclarationSyntax propertyDeclarationSyntax) { var propertyAccessibility = new Accessibility(propertyDeclarationSyntax.Modifiers); @@ -133,6 +170,74 @@ public static bool HasPublicSetter(this PropertyDeclarationSyntax propertyDeclar return setterAccessibility is { IsPublic: true, IsStatic: false }; } + public static bool IsDtoOrNullableDto(this PropertyDeclarationSyntax propertyDeclarationSyntax, + SyntaxNodeAnalysisContext context, List allowableTypes) + { + var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyDeclarationSyntax); + if (propertySymbol is null) + { + return false; + } + + var getter = propertySymbol.GetMethod; + if (getter is null) + { + return false; + } + + var returnType = propertySymbol.GetMethod!.ReturnType; + if (returnType.IsNullable(context)) + { + if (IsDto(returnType.WithoutNullable(context))) + { + return true; + } + } + + if (IsDto(returnType)) + { + return true; + } + + return false; + + bool IsDto(ITypeSymbol symbol) + { + if (symbol is not INamedTypeSymbol namedTypeSymbol) + { + return false; + } + + if (!namedTypeSymbol.IsReferenceType) //We dont accept any enums, or other value types + { + return false; + } + + if (namedTypeSymbol.IsStatic + || namedTypeSymbol.DeclaredAccessibility != Microsoft.CodeAnalysis.Accessibility.Public) + { + return false; + } + + if (!namedTypeSymbol.HasParameterlessConstructor()) + { + return false; + } + + if (!namedTypeSymbol.HasPublicGetterAndSetterProperties()) + { + return false; + } + + if (!namedTypeSymbol.HasPropertiesOfAllowableTypes(allowableTypes)) + { + return false; + } + + return true; + } + } + public static bool IsEmptyNode(this XmlNodeSyntax nodeSyntax) { if (nodeSyntax is XmlTextSyntax textSyntax) @@ -149,7 +254,7 @@ public static bool IsEmptyNode(this XmlNodeSyntax nodeSyntax) return true; } - public static bool IsEnumType(this PropertyDeclarationSyntax propertyDeclarationSyntax, + public static bool IsEnumOrNullableEnumType(this PropertyDeclarationSyntax propertyDeclarationSyntax, SyntaxNodeAnalysisContext context) { var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyDeclarationSyntax); @@ -165,6 +270,14 @@ public static bool IsEnumType(this PropertyDeclarationSyntax propertyDeclaration } var returnType = propertySymbol.GetMethod!.ReturnType; + if (returnType.IsNullable(context)) + { + if (returnType.WithoutNullable(context).IsEnum()) + { + return true; + } + } + if (returnType.IsEnum()) { return true; @@ -305,27 +418,6 @@ public static bool IsNotType(this ParameterSyntax parameterSyntax, Syntax return !isDerivedFrom; } - public static bool IsReferenceType(this PropertyDeclarationSyntax propertyDeclarationSyntax, - SyntaxNodeAnalysisContext context) - { - var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyDeclarationSyntax); - if (propertySymbol is null) - { - return false; - } - - var getter = propertySymbol.GetMethod; - if (getter is null) - { - return false; - } - - var returnType = getter.ReturnType; - - return returnType.IsReferenceType; - } - - public static bool IsNullableType(this PropertyDeclarationSyntax propertyDeclarationSyntax, SyntaxNodeAnalysisContext context) { @@ -441,6 +533,26 @@ public static bool IsPublicStaticMethod(this MethodDeclarationSyntax methodDecla return accessibility is { IsPublic: true, IsStatic: true }; } + public static bool IsReferenceType(this PropertyDeclarationSyntax propertyDeclarationSyntax, + SyntaxNodeAnalysisContext context) + { + var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyDeclarationSyntax); + if (propertySymbol is null) + { + return false; + } + + var getter = propertySymbol.GetMethod; + if (getter is null) + { + return false; + } + + var returnType = getter.ReturnType; + + return returnType.IsReferenceType; + } + public static bool IsRequired(this MemberDeclarationSyntax memberDeclarationSyntax) { var accessibility = new Accessibility(memberDeclarationSyntax.Modifiers); diff --git a/src/Tools.Analyzers.NonPlatform.UnitTests/DomainDrivenDesignAnalyzerSpec.cs b/src/Tools.Analyzers.NonPlatform.UnitTests/DomainDrivenDesignAnalyzerSpec.cs index 7e0a8018..73a2c614 100644 --- a/src/Tools.Analyzers.NonPlatform.UnitTests/DomainDrivenDesignAnalyzerSpec.cs +++ b/src/Tools.Analyzers.NonPlatform.UnitTests/DomainDrivenDesignAnalyzerSpec.cs @@ -4234,6 +4234,298 @@ public static AClassed Create() await Verify.NoDiagnosticExists(input); } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsNotRequiredAndNotInitializedAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public AnEnum AProperty { get; set; } +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumValueTypeIsRequiredAndNotInitializedAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + AProperty = AnEnum.AValue, + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required AnEnum AProperty { get; set; } +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsInitializedAndNotRequiredAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public AnEnum AProperty { get; set; } = AnEnum.AValue; +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsNullableAndNotRequiredAndNotInitialized_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public AnEnum? AProperty { get; set; } +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsNullableAndRequiredAndNotInitialized_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + AProperty = AnEnum.AValue, + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required AnEnum? AProperty { get; set; } +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsNullableAndInitializedAndNotRequired_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public AnEnum? AProperty { get; set; } = AnEnum.AValue; +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsInitializedAndRequiredAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + AProperty = AnEnum.AValue, + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required AnEnum AProperty { get; set; } = AnEnum.AValue; +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsInitializedAndNullableAndNotRequired_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public AnEnum? AProperty { get; set; } = AnEnum.AValue; +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyEnumTypeIsInitializedAndRequiredAndNullable_ThenNoAlert() + { + const string input = @" +using System; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + AProperty = AnEnum.AValue, + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required AnEnum? AProperty { get; set; } = AnEnum.AValue; +} +public enum AnEnum +{ + AValue +}"; + + await Verify.NoDiagnosticExists(input); + } } [Trait("Category", "Unit")] @@ -4472,7 +4764,6 @@ public static AClassed Create() { return new AClassed { - AProperty = AnEnum.AValue, RootId = string.Empty, OccurredUtc = DateTime.UtcNow }; @@ -4482,7 +4773,7 @@ public static AClassed Create() public required DateTime OccurredUtc { get; set; } - public required AnEnum AProperty { get; set; } + public AnEnum AProperty { get; set; } } public enum AnEnum { @@ -4552,6 +4843,83 @@ public static AClassed Create() await Verify.NoDiagnosticExists(input); } + + [Fact] + public async Task WhenAnyPropertyIsNotADto_ThenAlerts() + { + const string input = @" +using System; +using System.Collections.Generic; +using Domain.Interfaces.Entities; +namespace ANamespace; +public sealed class AClassed : IDomainEvent +{ + public static AClassed Create() + { + return new AClassed + { + AProperty = new ADto(), + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required ADto AProperty { get; set; } +} +public class ADto +{ + public string AProperty { get; } +}"; + + await Verify.DiagnosticExists( + DomainDrivenDesignAnalyzer.Rule049, input, 22, 26, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsADto_ThenNoAlert() + { + const string input = @" +using System; +using System.Collections.Generic; +using Domain.Interfaces.Entities; +using AnOtherNamespace; +namespace ANamespace +{ + public sealed class AClassed : IDomainEvent + { + public static AClassed Create() + { + return new AClassed + { + AProperty = new ADto { AProperty1 = string.Empty }, + RootId = string.Empty, + OccurredUtc = DateTime.UtcNow + }; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required ADto AProperty { get; set; } + } +} +namespace AnOtherNamespace +{ + public class ADto + { + public required string AProperty1 { get; set; } + + public string? AProperty2 { get; set; } + } +}"; + + await Verify.NoDiagnosticExists(input); + } } } } \ No newline at end of file diff --git a/src/Tools.Analyzers.NonPlatform.UnitTests/EventingAnalyzerSpec.cs b/src/Tools.Analyzers.NonPlatform.UnitTests/EventingAnalyzerSpec.cs new file mode 100644 index 00000000..6dc58edb --- /dev/null +++ b/src/Tools.Analyzers.NonPlatform.UnitTests/EventingAnalyzerSpec.cs @@ -0,0 +1,819 @@ +extern alias NonPlatformAnalyzers; +using Xunit; +using EventingAnalyzer = NonPlatformAnalyzers::Tools.Analyzers.NonPlatform.EventingAnalyzer; +using UsedImplicitly = NonPlatformAnalyzers::JetBrains.Annotations.UsedImplicitlyAttribute; + +namespace Tools.Analyzers.NonPlatform.UnitTests; + +[UsedImplicitly] +public class EventingAnalyzerSpec +{ + [Trait("Category", "Unit")] + public class GivenAnyRule + { + [Fact] + public async Task WhenInExcludedNamespace_ThenNoAlert() + { + const string input = @" +using Infrastructure.Web.Api.Interfaces; +namespace Common; +public sealed class AClass : IWebApiService +{ +}"; + + await Verify.NoDiagnosticExists(input); + } + } + + [UsedImplicitly] + public class GivenAnIntegrationEvent + { + [Trait("Category", "Unit")] + public class GivenAnyIntegrationEvent + { + [Fact] + public async Task WhenNoCtor_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + } + + [Trait("Category", "Unit")] + public class GivenRule010 + { + [Fact] + public async Task WhenIsNotPublic_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +internal sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule010, input, 5, 23, "AClass"); + } + } + + [Trait("Category", "Unit")] + public class GivenRule011 + { + [Fact] + public async Task WhenIsNotSealed_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule011, input, 5, 14, "AClass"); + } + } + + [Trait("Category", "Unit")] + public class GivenRule012 + { + [Fact] + public async Task WhenHasCtorAndNotParameterless_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public AClass(string rootId, DateTime occurredUtc) + { + RootId = rootId; + OccurredUtc = occurredUtc; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule012, input, 5, 21, "AClass"); + } + + [Fact] + public async Task WhenHasCtorAndPrivate_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + private AClass() + { + RootId = string.Empty; + OccurredUtc = DateTime.UtcNow; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule012, input, 5, 21, "AClass"); + } + + [Fact] + public async Task WhenHasCtorAndIsParameterless_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public AClass() + { + RootId = string.Empty; + OccurredUtc = DateTime.UtcNow; + } + + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + } + + [Trait("Category", "Unit")] + public class GivenRule013 + { + [Fact] + public async Task WhenAnyPropertyHasNoSetter_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule013, input, 11, 20, "AProperty", null); + } + } + + [Trait("Category", "Unit")] + public class GivenRule014 + { + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsNotRequiredAndNotInitializedAndNotNullable_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string AProperty { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule014, input, 11, 19, "AProperty"); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsRequiredAndNotInitializedAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required string AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsInitializedAndNotRequiredAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string AProperty { get; set; } = string.Empty; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsNullableAndNotRequiredAndNotInitialized_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsNullableAndRequiredAndNotInitialized_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsNullableAndInitializedAndNotRequired_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; set; } = string.Empty; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsInitializedAndRequiredAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required string AProperty { get; set; } = string.Empty; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsInitializedAndNullableAndNotRequired_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; set; } = string.Empty; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsInitializedAndRequiredAndNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; set; } = string.Empty; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsNotRequiredAndNotInitializedAndNotNullable_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public DateTime AProperty { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule014, input, 11, 21, "AProperty"); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsRequiredAndNotInitializedAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required DateTime AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsInitializedAndNotRequiredAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public DateTime AProperty { get; set; } = DateTime.UtcNow; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsNullableAndNotRequiredAndNotInitialized_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public DateTime? AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsNullableAndRequiredAndNotInitialized_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required DateTime? AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsNullableAndInitializedAndNotRequired_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public DateTime? AProperty { get; set; } = DateTime.UtcNow; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsInitializedAndRequiredAndNotNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required DateTime AProperty { get; set; } = DateTime.UtcNow; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsInitializedAndNullableAndNotRequired_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public DateTime? AProperty { get; set; } = DateTime.UtcNow; +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyValueTypeIsInitializedAndRequiredAndNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required DateTime? AProperty { get; set; } = DateTime.UtcNow; +}"; + + await Verify.NoDiagnosticExists(input); + } + } + + [Trait("Category", "Unit")] + public class GivenRule015 + { + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsOptional_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +using Common; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required Optional AProperty { get; set; } +}"; + + await Verify.DiagnosticExists(input, + (EventingAnalyzer.Rule015, 12, 38, "AProperty", null), + (EventingAnalyzer.Rule016, 12, 38, "AProperty", [GivenRule016.AllTypes])); + } + + [Fact] + public async Task WhenAnyPropertyReferenceTypeIsNullable_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public string? AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + } + + [Trait("Category", "Unit")] + public class GivenRule016 + { + public const string AllTypes = + "bool or string or ulong or int or long or double or decimal or System.DateTime or byte"; + + [Fact] + public async Task WhenAnyPropertyIsNotSupportedPrimitive_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required char AProperty { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule016, input, 11, 26, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsNotSupportedListOfPrimitive_ThenAlerts() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required List AProperty { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule016, input, 12, 32, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsNotSupportedDictionaryOfPrimitive_ThenAlerts() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required Dictionary AProperty { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule016, input, 12, 46, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsNotSupportedDictionaryKeyType_ThenAlerts() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required Dictionary AProperty { get; set; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule016, input, 12, 46, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsSupportedPrimitive_ThenNoAlert() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required string AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyIsEnum_ThenAlerts() + { + const string input = @" +using System; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required AnEnum AProperty { get; set; } +} +public enum AnEnum +{ + AValue +} +"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule016, input, 11, 28, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsSupportedListOfPrimitive_ThenNoAlert() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required List AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyIsSupportedDictionaryOfPrimitive_ThenNoAlert() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required Dictionary AProperty { get; set; } +}"; + + await Verify.NoDiagnosticExists(input); + } + + [Fact] + public async Task WhenAnyPropertyIsNotADto_ThenAlerts() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +namespace ANamespace; +public sealed class AClass : IIntegrationEvent +{ + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required ADto AProperty { get; set; } +} +public class ADto +{ + public string AProperty { get; } +}"; + + await Verify.DiagnosticExists( + EventingAnalyzer.Rule016, input, 12, 26, "AProperty", AllTypes); + } + + [Fact] + public async Task WhenAnyPropertyIsADto_ThenNoAlert() + { + const string input = @" +using System; +using System.Collections.Generic; +using Infrastructure.Eventing.Interfaces.Notifications; +using AnOtherNamespace; +namespace ANamespace +{ + public sealed class AClass : IIntegrationEvent + { + public required string RootId { get; set; } + + public required DateTime OccurredUtc { get; set; } + + public required ADto AProperty { get; set; } + } +} +namespace AnOtherNamespace +{ + public class ADto + { + public required string AProperty1 { get; set; } + + public string? AProperty2 { get; set; } + } +}"; + + await Verify.NoDiagnosticExists(input); + } + } + } +} \ No newline at end of file diff --git a/src/Tools.Analyzers.NonPlatform/AnalyzerReleases.Shipped.md b/src/Tools.Analyzers.NonPlatform/AnalyzerReleases.Shipped.md index d015254a..6c2a1d83 100644 --- a/src/Tools.Analyzers.NonPlatform/AnalyzerReleases.Shipped.md +++ b/src/Tools.Analyzers.NonPlatform/AnalyzerReleases.Shipped.md @@ -39,7 +39,7 @@ SAASDDD035 | SaaStackDDD | Error | ValueObjects must only have immutable methods SAASDDD036 | SaaStackDDD | Warning | ValueObjects should be marked as sealed. SAASDDD040 | SaaStackDDD | Error | DomainEvents must be public - SAASDDD041 | SaaStackDDD | Warning | DomainEvents must be sealed + SAASDDD041 | SaaStackDDD | Warning | DomainEvents should be sealed SAASDDD042 | SaaStackDDD | Error | DomainEvents must have a parameterless constructor SAASDDD043 | SaaStackDDD | Error | DomainEvents must be named in the past tense SAASDDD045 | SaaStackDDD | Error | Create() class factory methods must return correct types @@ -47,6 +47,13 @@ SAASDDD047 | SaaStackDDD | Error | Properties must be marked required or nullable or initialized SAASDDD048 | SaaStackDDD | Error | Properties must be nullable not Optional{T} SAASDDD049 | SaaStackDDD | Error | Properties must be of correct type + SAASEVT010 | SaaStackEventing | Error | IntegrationEvents must be public + SAASEVT011 | SaaStackEventing | Warning | IntegrationEvents should be sealed + SAASEVT012 | SaaStackEventing | Error | IntegrationEvents must have a parameterless constructor + SAASEVT013 | SaaStackEventing | Error | Properties must have public getters and setters + SAASEVT014 | SaaStackEventing | Error | Properties must be marked required or nullable or initialized + SAASEVT015 | SaaStackEventing | Error | Properties must be nullable not Optional{T} + SAASEVT016 | SaaStackEventing | Error | Properties must be of correct type SAASAPP010 | SaaStackApplication | Error | Resources must be public SAASAPP011 | SaaStackApplication | Error | Resources must have a parameterless constructor SAASAPP012 | SaaStackApplication | Error | Properties must have public getters and setters diff --git a/src/Tools.Analyzers.NonPlatform/DomainDrivenDesignAnalyzer.cs b/src/Tools.Analyzers.NonPlatform/DomainDrivenDesignAnalyzer.cs index 1138c884..531dabdd 100644 --- a/src/Tools.Analyzers.NonPlatform/DomainDrivenDesignAnalyzer.cs +++ b/src/Tools.Analyzers.NonPlatform/DomainDrivenDesignAnalyzer.cs @@ -48,7 +48,7 @@ namespace Tools.Analyzers.NonPlatform; /// SAASDDD036: Warning: ValueObjects should be marked as sealed /// DomainEvents: /// SAASDDD040: Error: DomainEvents must be public -/// SAASDDD041: Warning: DomainEvents must be sealed +/// SAASDDD041: Warning: DomainEvents should be sealed /// SAASDDD042: Error: DomainEvents must have a parameterless constructor /// SAASDDD043: Information: DomainEvents must be named in the past tense /// SAASDDD044: Error: DomainEvents must have at least one Create() class factory method @@ -57,7 +57,7 @@ namespace Tools.Analyzers.NonPlatform; /// SAASDDD047: Error: Properties must be required or nullable or initialized /// SAASDDD048: Error: Properties must be nullable, not Optional{T} for interoperability /// SAASDDD049: Error: Properties must have return type of primitives, List{TPrimitive}, Dictionary{string,TPrimitive}, -/// or be enums +/// or be enums, or other DTOs /// [DiagnosticAnalyzer(LanguageNames.CSharp)] public class DomainDrivenDesignAnalyzer : DiagnosticAnalyzer @@ -164,17 +164,17 @@ public class DomainDrivenDesignAnalyzer : DiagnosticAnalyzer AnalyzerConstants.Categories.Ddd, nameof(Resources.SAASDDD035Title), nameof(Resources.SAASDDD035Description), nameof(Resources.SAASDDD035MessageFormat)); internal static readonly DiagnosticDescriptor Rule036 = "SAASDDD036".GetDescriptor(DiagnosticSeverity.Warning, - AnalyzerConstants.Categories.Ddd, nameof(Resources.Diagnostic_Title_ClassMustBeSealed), - nameof(Resources.Diagnostic_Description_ClassMustBeSealed), - nameof(Resources.Diagnostic_MessageFormat_ClassMustBeSealed)); + AnalyzerConstants.Categories.Ddd, nameof(Resources.Diagnostic_Title_ClassShouldBeSealed), + nameof(Resources.Diagnostic_Description_ClassShouldBeSealed), + nameof(Resources.Diagnostic_MessageFormat_ClassShouldBeSealed)); internal static readonly DiagnosticDescriptor Rule040 = "SAASDDD040".GetDescriptor(DiagnosticSeverity.Error, AnalyzerConstants.Categories.Ddd, nameof(Resources.Diagnostic_Title_ClassMustBePublic), nameof(Resources.Diagnostic_Description_ClassMustBePublic), nameof(Resources.Diagnostic_MessageFormat_ClassMustBePublic)); internal static readonly DiagnosticDescriptor Rule041 = "SAASDDD041".GetDescriptor(DiagnosticSeverity.Warning, - AnalyzerConstants.Categories.Ddd, nameof(Resources.Diagnostic_Title_ClassMustBeSealed), - nameof(Resources.Diagnostic_Description_ClassMustBeSealed), - nameof(Resources.Diagnostic_MessageFormat_ClassMustBeSealed)); + AnalyzerConstants.Categories.Ddd, nameof(Resources.Diagnostic_Title_ClassShouldBeSealed), + nameof(Resources.Diagnostic_Description_ClassShouldBeSealed), + nameof(Resources.Diagnostic_MessageFormat_ClassShouldBeSealed)); internal static readonly DiagnosticDescriptor Rule042 = "SAASDDD042".GetDescriptor(DiagnosticSeverity.Error, AnalyzerConstants.Categories.Ddd, nameof(Resources.Diagnostic_Title_ClassMustHaveParameterlessConstructor), nameof(Resources.Diagnostic_Description_ClassMustHaveParameterlessConstructor), @@ -553,7 +553,9 @@ private static void AnalyzeDomainEvent(SyntaxNodeAnalysisContext context) context.ReportDiagnostic(Rule046, property); } - if (!property.IsRequired() && !property.IsInitialized()) + if (!property.IsEnumOrNullableEnumType(context) + && !property.IsRequired() + && !property.IsInitialized()) { if (!property.IsNullableType(context)) { @@ -568,7 +570,8 @@ private static void AnalyzeDomainEvent(SyntaxNodeAnalysisContext context) var allowedReturnTypes = context.GetAllowableDomainEventPropertyReturnTypes(); if (context.HasIncorrectReturnType(property, allowedReturnTypes) - && !property.IsEnumType(context)) + && !property.IsEnumOrNullableEnumType(context) + && !property.IsDtoOrNullableDto(context, allowedReturnTypes.ToList())) { var acceptableReturnTypes = allowedReturnTypes diff --git a/src/Tools.Analyzers.NonPlatform/EventingAnalyzer.cs b/src/Tools.Analyzers.NonPlatform/EventingAnalyzer.cs new file mode 100644 index 00000000..6a586b32 --- /dev/null +++ b/src/Tools.Analyzers.NonPlatform/EventingAnalyzer.cs @@ -0,0 +1,190 @@ +using System.Collections.Immutable; +using Common.Extensions; +using Infrastructure.Eventing.Interfaces.Notifications; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Tools.Analyzers.Common; +using Tools.Analyzers.Common.Extensions; +using Tools.Analyzers.NonPlatform.Extensions; + +namespace Tools.Analyzers.NonPlatform; + +/// +/// An analyzer to correct the implementation of eventing: +/// IntegrationEvents: +/// SAASEVT010: Error: IntegrationEvents must be public +/// SAASEVT011: Warning: IntegrationEvents should be sealed +/// SAASEVT012: Error: IntegrationEvents must have a parameterless constructor +/// SAASEVT013: Error: Properties must have public getters and setters +/// SAASEVT014: Error: Properties must be required or nullable or initialized +/// SAASEVT015: Error: Properties must be nullable, not Optional{T} for interoperability +/// SAASEVT016: Error: Properties must have return type of primitives, List{TPrimitive}, Dictionary{string,TPrimitive}, +/// or be other DTOs +/// +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class EventingAnalyzer : DiagnosticAnalyzer +{ + internal static readonly SpecialType[] AllowableIntegrationEventPrimitives = + { + SpecialType.System_Boolean, + SpecialType.System_String, + SpecialType.System_UInt64, + SpecialType.System_Int32, + SpecialType.System_Int64, + SpecialType.System_Double, + SpecialType.System_Decimal, + SpecialType.System_DateTime, + SpecialType.System_Byte + }; + internal static readonly DiagnosticDescriptor Rule010 = "SAASEVT010".GetDescriptor(DiagnosticSeverity.Error, + AnalyzerConstants.Categories.Eventing, nameof(Resources.Diagnostic_Title_ClassMustBePublic), + nameof(Resources.Diagnostic_Description_ClassMustBePublic), + nameof(Resources.Diagnostic_MessageFormat_ClassMustBePublic)); + internal static readonly DiagnosticDescriptor Rule011 = "SAASEVT011".GetDescriptor(DiagnosticSeverity.Warning, + AnalyzerConstants.Categories.Eventing, nameof(Resources.Diagnostic_Title_ClassShouldBeSealed), + nameof(Resources.Diagnostic_Description_ClassShouldBeSealed), + nameof(Resources.Diagnostic_MessageFormat_ClassShouldBeSealed)); + internal static readonly DiagnosticDescriptor Rule012 = "SAASEVT012".GetDescriptor(DiagnosticSeverity.Error, + AnalyzerConstants.Categories.Eventing, nameof(Resources.Diagnostic_Title_ClassMustHaveParameterlessConstructor), + nameof(Resources.Diagnostic_Description_ClassMustHaveParameterlessConstructor), + nameof(Resources.Diagnostic_MessageFormat_ClassMustHaveParameterlessConstructor)); + internal static readonly DiagnosticDescriptor Rule013 = "SAASEVT013".GetDescriptor(DiagnosticSeverity.Error, + AnalyzerConstants.Categories.Eventing, nameof(Resources.Diagnostic_Title_PropertyMustBeGettableAndSettable), + nameof(Resources.Diagnostic_Description_PropertyMustBeGettableAndSettable), + nameof(Resources.Diagnostic_MessageFormat_PropertyMustBeGettableAndSettable)); + internal static readonly DiagnosticDescriptor Rule014 = "SAASEVT014".GetDescriptor(DiagnosticSeverity.Error, + AnalyzerConstants.Categories.Eventing, nameof(Resources.SAASEVT014Title), + nameof(Resources.SAASEVT014Description), nameof(Resources.SAASEVT014MessageFormat)); + internal static readonly DiagnosticDescriptor Rule015 = "SAASEVT015".GetDescriptor(DiagnosticSeverity.Error, + AnalyzerConstants.Categories.Eventing, nameof(Resources.Diagnostic_Title_PropertyMustBeNullableNotOptional), + nameof(Resources.Diagnostic_Description_PropertyMustBeNullableNotOptional), + nameof(Resources.Diagnostic_MessageFormat_PropertyMustBeNullableNotOptional)); + internal static readonly DiagnosticDescriptor Rule016 = "SAASEVT016".GetDescriptor(DiagnosticSeverity.Error, + AnalyzerConstants.Categories.Eventing, nameof(Resources.SAASEVT016Title), + nameof(Resources.SAASEVT016Description), nameof(Resources.SAASEVT016MessageFormat)); + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create( + Rule010, Rule011, Rule012, Rule013, Rule014, Rule015, Rule016); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeIntegrationEvent, SyntaxKind.ClassDeclaration); + } + + private static void AnalyzeIntegrationEvent(SyntaxNodeAnalysisContext context) + { + var methodSyntax = context.Node; + if (methodSyntax is not ClassDeclarationSyntax classDeclarationSyntax) + { + return; + } + + if (context.IsExcludedInNamespace(classDeclarationSyntax, AnalyzerConstants.PlatformNamespaces)) + { + return; + } + + if (classDeclarationSyntax.IsNotType(context)) + { + return; + } + + if (!classDeclarationSyntax.IsPublic()) + { + context.ReportDiagnostic(Rule010, classDeclarationSyntax); + } + + if (!classDeclarationSyntax.IsSealed()) + { + context.ReportDiagnostic(Rule011, classDeclarationSyntax); + } + + if (!classDeclarationSyntax.HasParameterlessConstructor()) + { + context.ReportDiagnostic(Rule012, classDeclarationSyntax); + } + + var allProperties = classDeclarationSyntax.Members.Where(member => member is PropertyDeclarationSyntax) + .Cast() + .ToList(); + if (allProperties.HasAny()) + { + foreach (var property in allProperties) + { + if (!property.HasPublicGetterAndSetter()) + { + context.ReportDiagnostic(Rule013, property); + } + + if (!property.IsRequired() + && !property.IsInitialized()) + { + if (!property.IsNullableType(context)) + { + context.ReportDiagnostic(Rule014, property); + } + } + + if (!property.IsNullableType(context) && property.IsOptionalType(context)) + { + context.ReportDiagnostic(Rule015, property); + } + + var allowedReturnTypes = context.GetAllowableIntegrationEventPropertyReturnTypes(); + if (context.HasIncorrectReturnType(property, allowedReturnTypes) + && !property.IsDtoOrNullableDto(context, allowedReturnTypes.ToList())) + { + var acceptableReturnTypes = + allowedReturnTypes + .Where(allowable => + !allowable.ToDisplayString().StartsWith("System.Collections") + && !allowable.ToDisplayString().EndsWith("?")) + .Select(allowable => allowable.ToDisplayString()).Join(" or "); + context.ReportDiagnostic(Rule016, property, acceptableReturnTypes); + } + } + } + } +} + +internal static class EventingExtensions +{ + private static INamedTypeSymbol[]? _allowableIntegrationEventPropertyReturnTypes; + + public static INamedTypeSymbol[] GetAllowableIntegrationEventPropertyReturnTypes( + this SyntaxNodeAnalysisContext context) + { + // Cache this + if (_allowableIntegrationEventPropertyReturnTypes is null) + { + var stringType = context.Compilation.GetSpecialType(SpecialType.System_String); + var primitiveTypes = EventingAnalyzer.AllowableIntegrationEventPrimitives + .Select(context.Compilation.GetSpecialType).ToArray(); + + var nullableOfType = context.Compilation.GetTypeByMetadataName(typeof(Nullable<>).FullName!)!; + var nullableTypes = primitiveTypes + .Select(primitive => nullableOfType.Construct(primitive)).ToArray(); + + var listOfType = context.Compilation.GetTypeByMetadataName(typeof(List<>).FullName!)!; + var listTypes = primitiveTypes + .Select(primitive => listOfType.Construct(primitive)).ToArray(); + + var dictionaryOfType = context.Compilation.GetTypeByMetadataName(typeof(Dictionary<,>).FullName!)!; + var stringDictionaryTypes = primitiveTypes + .Select(primitive => dictionaryOfType.Construct(stringType, primitive)) + .ToArray(); + + _allowableIntegrationEventPropertyReturnTypes = primitiveTypes + .Concat(nullableTypes) + .Concat(listTypes) + .Concat(stringDictionaryTypes).ToArray(); + } + + return _allowableIntegrationEventPropertyReturnTypes; + } +} \ No newline at end of file diff --git a/src/Tools.Analyzers.NonPlatform/Extensions/SyntaxExtensions.cs b/src/Tools.Analyzers.NonPlatform/Extensions/SyntaxExtensions.cs index 648ed8d0..6c9b9b86 100644 --- a/src/Tools.Analyzers.NonPlatform/Extensions/SyntaxExtensions.cs +++ b/src/Tools.Analyzers.NonPlatform/Extensions/SyntaxExtensions.cs @@ -46,24 +46,6 @@ public static bool HasOnlyPrivateInstanceConstructors(this ClassDeclarationSynta return true; } - public static bool HasParameterlessConstructor(this ClassDeclarationSyntax classDeclarationSyntax) - { - var allConstructors = classDeclarationSyntax.Members.Where(member => member is ConstructorDeclarationSyntax) - .Cast() - .ToList(); - if (allConstructors.HasAny()) - { - var parameterlessConstructors = allConstructors - .Where(constructor => constructor.ParameterList.Parameters.Count == 0 && constructor.IsPublic()); - if (parameterlessConstructors.HasNone()) - { - return false; - } - } - - return true; - } - public static bool HasRouteAttribute(this ClassDeclarationSyntax classDeclarationSyntax, SyntaxNodeAnalysisContext context) { diff --git a/src/Tools.Analyzers.NonPlatform/Resources.Designer.cs b/src/Tools.Analyzers.NonPlatform/Resources.Designer.cs index c7bdab53..e16a0146 100644 --- a/src/Tools.Analyzers.NonPlatform/Resources.Designer.cs +++ b/src/Tools.Analyzers.NonPlatform/Resources.Designer.cs @@ -159,7 +159,7 @@ internal static string Diagnostic_Description_ClassMustBePublic { } /// - /// Looks up a localized string similar to Class should be marked as 'sealed'.. + /// Looks up a localized string similar to Class must be marked as 'sealed'.. /// internal static string Diagnostic_Description_ClassMustBeSealed { get { @@ -176,6 +176,15 @@ internal static string Diagnostic_Description_ClassMustHaveParameterlessConstruc } } + /// + /// Looks up a localized string similar to Class should be marked as 'sealed'.. + /// + internal static string Diagnostic_Description_ClassShouldBeSealed { + get { + return ResourceManager.GetString("Diagnostic_Description_ClassShouldBeSealed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Constructor must be 'private'.. /// @@ -267,7 +276,7 @@ internal static string Diagnostic_MessageFormat_ClassMustBePublic { } /// - /// Looks up a localized string similar to Class '{0}' should be marked as 'sealed'. + /// Looks up a localized string similar to Class '{0}' must be marked as 'sealed'. /// internal static string Diagnostic_MessageFormat_ClassMustBeSealed { get { @@ -284,6 +293,15 @@ internal static string Diagnostic_MessageFormat_ClassMustHaveParameterlessConstr } } + /// + /// Looks up a localized string similar to Class '{0}' should be marked as 'sealed'. + /// + internal static string Diagnostic_MessageFormat_ClassShouldBeSealed { + get { + return ResourceManager.GetString("Diagnostic_MessageFormat_ClassShouldBeSealed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Constructor '{0}' must be 'private'. /// @@ -375,7 +393,7 @@ internal static string Diagnostic_Title_ClassMustBePublic { } /// - /// Looks up a localized string similar to Class should be marked as 'sealed'. + /// Looks up a localized string similar to Class must be marked as 'sealed'. /// internal static string Diagnostic_Title_ClassMustBeSealed { get { @@ -392,6 +410,15 @@ internal static string Diagnostic_Title_ClassMustHaveParameterlessConstructor { } } + /// + /// Looks up a localized string similar to Class should be marked as 'sealed'. + /// + internal static string Diagnostic_Title_ClassShouldBeSealed { + get { + return ResourceManager.GetString("Diagnostic_Title_ClassShouldBeSealed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Wrong accessibility. /// @@ -798,7 +825,7 @@ internal static string SAASDDD049Description { } /// - /// Looks up a localized string similar to Property '{0}' must return one of these primitive types: '{1}', or any Enum, or a List<T>/Dictionary<string, T> of one of those types. + /// Looks up a localized string similar to Property '{0}' must return one of these primitive types: '{1}', or any Enum, or a List<T>/Dictionary<string, T> of one of those types, or another DTO. /// internal static string SAASDDD049MessageFormat { get { @@ -815,6 +842,60 @@ internal static string SAASDDD049Title { } } + /// + /// Looks up a localized string similar to Property must be marked 'required' or be nullable or be initialized.. + /// + internal static string SAASEVT014Description { + get { + return ResourceManager.GetString("SAASEVT014Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Property '{0}' must be marked 'required' or be nullable or be initialized. + /// + internal static string SAASEVT014MessageFormat { + get { + return ResourceManager.GetString("SAASEVT014MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wrong declaration. + /// + internal static string SAASEVT014Title { + get { + return ResourceManager.GetString("SAASEVT014Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Property must have the return the correct type.. + /// + internal static string SAASEVT016Description { + get { + return ResourceManager.GetString("SAASEVT016Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Property '{0}' must return one of these primitive types: '{1}', or a List<T>/Dictionary<string, T> of one of those types, or another DTO. + /// + internal static string SAASEVT016MessageFormat { + get { + return ResourceManager.GetString("SAASEVT016MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wrong return type. + /// + internal static string SAASEVT016Title { + get { + return ResourceManager.GetString("SAASEVT016Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Aggregate or Entity should be registered with an identity prefix. /// diff --git a/src/Tools.Analyzers.NonPlatform/Resources.resx b/src/Tools.Analyzers.NonPlatform/Resources.resx index c75cb6f1..9386c1a1 100644 --- a/src/Tools.Analyzers.NonPlatform/Resources.resx +++ b/src/Tools.Analyzers.NonPlatform/Resources.resx @@ -79,15 +79,24 @@ Property must not be settable - + Class should be marked as 'sealed'. - + Class '{0}' should be marked as 'sealed' - + Class should be marked as 'sealed' + + Class must be marked as 'sealed'. + + + Class '{0}' must be marked as 'sealed' + + + Class must be marked as 'sealed' + Class should be 'public'. @@ -355,12 +364,31 @@ Property must have the return the correct type. - Property '{0}' must return one of these primitive types: '{1}', or any Enum, or a List<T>/Dictionary<string, T> of one of those types + Property '{0}' must return one of these primitive types: '{1}', or any Enum, or a List<T>/Dictionary<string, T> of one of those types, or another DTO Wrong return type + + Property must be marked 'required' or be nullable or be initialized. + + + Property '{0}' must be marked 'required' or be nullable or be initialized + + + Wrong declaration + + + Property must have the return the correct type. + + + Property '{0}' must return one of these primitive types: '{1}', or a List<T>/Dictionary<string, T> of one of those types, or another DTO + + + Wrong return type + + Property must have the return the correct type diff --git a/src/Tools.Analyzers.NonPlatform/Tools.Analyzers.NonPlatform.csproj b/src/Tools.Analyzers.NonPlatform/Tools.Analyzers.NonPlatform.csproj index 28186236..e215436a 100644 --- a/src/Tools.Analyzers.NonPlatform/Tools.Analyzers.NonPlatform.csproj +++ b/src/Tools.Analyzers.NonPlatform/Tools.Analyzers.NonPlatform.csproj @@ -227,6 +227,9 @@ Reference\Domain.Interfaces\DomainFactories.cs + + Reference\Domain.Common\DomainEvent.cs + Reference\Domain.Common\Resources.Designer.cs @@ -293,6 +296,9 @@ Reference\Infrastructure.Web.Hosting.Common\ISubdomainModule.cs + + Reference\Infrastructure.Eventing.Interfaces\Notifications\IIntegrationEvent.cs + diff --git a/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs b/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs new file mode 100644 index 00000000..620e9189 --- /dev/null +++ b/src/UserProfilesApplication.UnitTests/UserProfileApplication.DomainEventHandlersSpec.cs @@ -0,0 +1,142 @@ +using Application.Interfaces; +using Common; +using Domain.Common.Identity; +using Domain.Common.ValueObjects; +using Domain.Interfaces.Entities; +using Domain.Shared; +using Domain.Shared.EndUsers; +using EndUsersDomain; +using Moq; +using UnitTesting.Common; +using UserProfilesApplication.Persistence; +using UserProfilesDomain; +using Xunit; +using Events = EndUsersDomain.Events; +using PersonName = Domain.Shared.PersonName; + +namespace UserProfilesApplication.UnitTests; + +[Trait("Category", "Unit")] +public class UserProfileApplicationDomainEventHandlersSpec +{ + private readonly UserProfilesApplication _application; + private readonly Mock _caller; + private readonly Mock _idFactory; + private readonly Mock _recorder; + private readonly Mock _repository; + + public UserProfileApplicationDomainEventHandlersSpec() + { + _recorder = new Mock(); + _caller = new Mock(); + _idFactory = new Mock(); + _idFactory.Setup(idf => idf.Create(It.IsAny())) + .Returns("anid".ToId()); + _repository = new Mock(); + _repository.Setup(rep => rep.FindByUserIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Optional.None); + _repository.Setup(rep => rep.FindByEmailAddressAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(Optional.None); + _repository.Setup(rep => rep.SaveAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync((UserProfileRoot root, CancellationToken _) => root); + + _application = new UserProfilesApplication(_recorder.Object, _idFactory.Object, _repository.Object); + } + + [Fact] + public async Task WhenHandleEndUserRegisteredAsyncForPersonWButNoEmail_ThenReturnsError() + { + var domainEvent = Events.Registered("apersonid".ToId(), EndUserProfile.Create("afirstname").Value, + Optional.None, UserClassification.Person, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, + CancellationToken.None); + + result.Should().BeError(ErrorCode.RuleViolation, Resources.UserProfilesApplication_PersonMustHaveEmailAddress); + } + + [Fact] + public async Task WhenHandleEndUserRegisteredAsyncForAnyAndExistsForUserId_ThenReturnsError() + { + var user = UserProfileRoot.Create(_recorder.Object, _idFactory.Object, ProfileType.Person, "auserid".ToId(), + PersonName.Create("afirstname", "alastname").Value).Value; + _repository.Setup(rep => rep.FindByUserIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(user.ToOptional()); + var domainEvent = Events.Registered("apersonid".ToId(), EndUserProfile.Create("afirstname").Value, + EmailAddress.Create("auser@company.com").Value, UserClassification.Person, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, + CancellationToken.None); + + result.Should().BeError(ErrorCode.EntityExists, Resources.UserProfilesApplication_ProfileExistsForUser); + } + + [Fact] + public async Task WhenHandleEndUserRegisteredAsyncForAnyAndExistsForEmailAddress_ThenReturnsError() + { + var user = UserProfileRoot.Create(_recorder.Object, _idFactory.Object, ProfileType.Person, "auserid".ToId(), + PersonName.Create("afirstname", "alastname").Value).Value; + _repository.Setup(rep => rep.FindByEmailAddressAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(user.ToOptional()); + var domainEvent = Events.Registered("apersonid".ToId(), EndUserProfile.Create("afirstname").Value, + EmailAddress.Create("auser@company.com").Value, UserClassification.Person, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = + await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeError(ErrorCode.EntityExists, Resources.UserProfilesApplication_ProfileExistsForEmailAddress); + } + + [Fact] + public async Task WhenCreateProfileAsyncForMachine_ThenCreatesProfile() + { + var domainEvent = Events.Registered("amachineid".ToId(), EndUserProfile.Create("afirstname").Value, + EmailAddress.Create("amachine@company.com").Value, UserClassification.Machine, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = + await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeSuccess(); + _repository.Verify(rep => rep.SaveAsync(It.Is(up => + up.UserId == "amachineid".ToId() + && up.Type == ProfileType.Machine + && up.DisplayName.Value.Text == "afirstname" + && up.Name.Value.FirstName == "afirstname" + && up.Name.Value.LastName.HasValue == false + && up.EmailAddress.HasValue == false + && up.PhoneNumber.HasValue == false + && up.Address.CountryCode == CountryCodes.Default + && up.Timezone == Timezones.Default + && up.AvatarUrl.HasValue == false + ), It.IsAny())); + } + + [Fact] + public async Task WhenHandleEndUserRegisteredAsyncForPerson_ThenCreatesProfile() + { + var domainEvent = Events.Registered("apersonid".ToId(), EndUserProfile.Create("afirstname", "alastname").Value, + EmailAddress.Create("auser@company.com").Value, UserClassification.Person, UserAccess.Enabled, + UserStatus.Registered, Roles.Empty, Features.Empty); + + var result = + await _application.HandleEndUserRegisteredAsync(_caller.Object, domainEvent, CancellationToken.None); + + result.Should().BeSuccess(); + _repository.Verify(rep => rep.SaveAsync(It.Is(up => + up.UserId == "apersonid".ToId() + && up.Type == ProfileType.Person + && up.DisplayName.Value.Text == "afirstname" + && up.Name.Value.FirstName == "afirstname" + && up.Name.Value.LastName.Value == "alastname" + && up.EmailAddress.Value == "auser@company.com" + && up.PhoneNumber.HasValue == false + && up.Address.CountryCode == CountryCodes.Default + && up.Timezone == Timezones.Default + && up.AvatarUrl.HasValue == false + ), It.IsAny())); + } +} \ No newline at end of file diff --git a/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs b/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs index ce994d2d..dd5423e5 100644 --- a/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs +++ b/src/UserProfilesApplication.UnitTests/UserProfileApplicationSpec.cs @@ -1,5 +1,4 @@ using Application.Interfaces; -using Application.Resources.Shared; using Common; using Domain.Common.Identity; using Domain.Common.ValueObjects; @@ -43,103 +42,6 @@ public UserProfileApplicationSpec() _application = new UserProfilesApplication(_recorder.Object, _idFactory.Object, _repository.Object); } - [Fact] - public async Task WhenCreateProfileForAnyAndExistsForUserId_ThenReturnsError() - { - var user = UserProfileRoot.Create(_recorder.Object, _idFactory.Object, ProfileType.Person, "auserid".ToId(), - PersonName.Create("afirstname", "alastname").Value).Value; - _repository.Setup(rep => rep.FindByUserIdAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(user.ToOptional()); - - var result = await _application.CreateProfileAsync(_caller.Object, UserProfileClassification.Person, - "apersonid", - "anemailaddress", "afirstname", "alastname", Timezones.Default.ToString(), CountryCodes.Default.ToString(), - CancellationToken.None); - - result.Should().BeError(ErrorCode.EntityExists, Resources.UserProfilesApplication_ProfileExistsForUser); - } - - [Fact] - public async Task WhenCreateProfileForAnyAndExistsForEmailAddress_ThenReturnsError() - { - var user = UserProfileRoot.Create(_recorder.Object, _idFactory.Object, ProfileType.Person, "auserid".ToId(), - PersonName.Create("afirstname", "alastname").Value).Value; - _repository.Setup(rep => rep.FindByEmailAddressAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(user.ToOptional()); - - var result = await _application.CreateProfileAsync(_caller.Object, UserProfileClassification.Person, - "apersonid", - "auser@company.com", "afirstname", "alastname", Timezones.Default.ToString(), - CountryCodes.Default.ToString(), - CancellationToken.None); - - result.Should().BeError(ErrorCode.EntityExists, Resources.UserProfilesApplication_ProfileExistsForEmailAddress); - } - - [Fact] - public async Task WhenCreateProfileAsyncForMachine_ThenCreatesProfile() - { - var result = await _application.CreateProfileAsync(_caller.Object, UserProfileClassification.Machine, - "amachineid", - "anemailaddress", "afirstname", "alastname", Timezones.Default.ToString(), CountryCodes.Default.ToString(), - CancellationToken.None); - - result.Value.UserId.Should().Be("amachineid".ToId()); - result.Value.Classification.Should().Be(UserProfileClassification.Machine); - result.Value.DisplayName.Should().Be("afirstname"); - result.Value.Name.FirstName.Should().Be("afirstname"); - result.Value.Name.LastName.Should().BeNull(); - result.Value.EmailAddress.Should().BeNull(); - result.Value.PhoneNumber.Should().BeNull(); - result.Value.Address.CountryCode.Should().Be(CountryCodes.Default.ToString()); - result.Value.Timezone.Should().Be(Timezones.Default.ToString()); - result.Value.AvatarUrl.Should().BeNull(); - _repository.Verify(rep => rep.SaveAsync(It.Is(up => - up.UserId == "amachineid".ToId() - && up.Type == ProfileType.Machine - && up.DisplayName.Value.Text == "afirstname" - && up.Name.Value.FirstName == "afirstname" - && up.Name.Value.LastName.HasValue == false - && up.EmailAddress.HasValue == false - && up.PhoneNumber.HasValue == false - && up.Address.CountryCode == CountryCodes.Default - && up.Timezone == Timezones.Default - && up.AvatarUrl.HasValue == false - ), It.IsAny())); - } - - [Fact] - public async Task WhenCreateProfileAsyncForPerson_ThenCreatesProfile() - { - var result = await _application.CreateProfileAsync(_caller.Object, UserProfileClassification.Person, - "apersonid", - "auser@company.com", "afirstname", "alastname", Timezones.Default.ToString(), - CountryCodes.Default.ToString(), CancellationToken.None); - - result.Value.UserId.Should().Be("apersonid".ToId()); - result.Value.Classification.Should().Be(UserProfileClassification.Person); - result.Value.DisplayName.Should().Be("afirstname"); - result.Value.Name.FirstName.Should().Be("afirstname"); - result.Value.Name.LastName.Should().Be("alastname"); - result.Value.EmailAddress.Should().Be("auser@company.com"); - result.Value.PhoneNumber.Should().BeNull(); - result.Value.Address.CountryCode.Should().Be(CountryCodes.Default.ToString()); - result.Value.Timezone.Should().Be(Timezones.Default.ToString()); - result.Value.AvatarUrl.Should().BeNull(); - _repository.Verify(rep => rep.SaveAsync(It.Is(up => - up.UserId == "apersonid".ToId() - && up.Type == ProfileType.Person - && up.DisplayName.Value.Text == "afirstname" - && up.Name.Value.FirstName == "afirstname" - && up.Name.Value.LastName.Value == "alastname" - && up.EmailAddress.Value == "auser@company.com" - && up.PhoneNumber.HasValue == false - && up.Address.CountryCode == CountryCodes.Default - && up.Timezone == Timezones.Default - && up.AvatarUrl.HasValue == false - ), It.IsAny())); - } - [Fact] public async Task WhenFindPersonByEmailAddressAsyncAndNotExists_ThenReturns() { @@ -294,7 +196,28 @@ public async Task WhenGetProfileAsyncAndNotExists_ThenReturnsError() } [Fact] - public async Task WhenGetProfileAsync_ThenReturnsProfile() + public async Task WhenGetProfileAsyncByServiceAccount_ThenReturnsProfile() + { + _caller.Setup(cc => cc.IsServiceAccount) + .Returns(true); + + var profile = UserProfileRoot.Create(_recorder.Object, _idFactory.Object, ProfileType.Person, "auserid".ToId(), + PersonName.Create("afirstname", "alastname").Value).Value; + _repository.Setup(rep => rep.FindByUserIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(profile.ToOptional()); + + var result = await _application.GetProfileAsync(_caller.Object, "auserid", CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.FirstName.Should().Be("afirstname"); + result.Value.Name.LastName.Should().Be("alastname"); + result.Value.DisplayName.Should().Be("afirstname"); + result.Value.Timezone.Should().Be(Timezones.Default.ToString()); + result.Value.Address.CountryCode.Should().Be(CountryCodes.Default.ToString()); + } + + [Fact] + public async Task WhenGetProfileAsyncByOwner_ThenReturnsProfile() { _caller.Setup(cc => cc.CallerId) .Returns("auserid"); diff --git a/src/UserProfilesApplication.UnitTests/UserProfilesApplication.UnitTests.csproj b/src/UserProfilesApplication.UnitTests/UserProfilesApplication.UnitTests.csproj index 70dd1b32..e23e5fed 100644 --- a/src/UserProfilesApplication.UnitTests/UserProfilesApplication.UnitTests.csproj +++ b/src/UserProfilesApplication.UnitTests/UserProfilesApplication.UnitTests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/UserProfilesApplication/IUserProfilesApplication.DomainEventHandlers.cs b/src/UserProfilesApplication/IUserProfilesApplication.DomainEventHandlers.cs new file mode 100644 index 00000000..35303fbe --- /dev/null +++ b/src/UserProfilesApplication/IUserProfilesApplication.DomainEventHandlers.cs @@ -0,0 +1,11 @@ +using Application.Interfaces; +using Common; +using Domain.Events.Shared.EndUsers; + +namespace UserProfilesApplication; + +partial interface IUserProfilesApplication +{ + Task> HandleEndUserRegisteredAsync(ICallerContext caller, Registered domainEvent, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/UserProfilesApplication/IUserProfilesApplication.cs b/src/UserProfilesApplication/IUserProfilesApplication.cs index 535a0eea..475b2afa 100644 --- a/src/UserProfilesApplication/IUserProfilesApplication.cs +++ b/src/UserProfilesApplication/IUserProfilesApplication.cs @@ -4,7 +4,7 @@ namespace UserProfilesApplication; -public interface IUserProfilesApplication +public partial interface IUserProfilesApplication { Task> ChangeContactAddressAsync(ICallerContext caller, string userId, string? line1, string? line2, @@ -15,11 +15,6 @@ Task> ChangeProfileAsync(ICallerContext caller, strin string? lastName, string? displayName, string? phoneNumber, string? timezone, CancellationToken cancellationToken); - Task> CreateProfileAsync(ICallerContext caller, UserProfileClassification classification, - string userId, - string? emailAddress, string firstName, string? lastName, string? timezone, string? countryCode, - CancellationToken cancellationToken); - Task, Error>> FindPersonByEmailAddressAsync(ICallerContext caller, string emailAddress, CancellationToken cancellationToken); diff --git a/src/UserProfilesApplication/UserProfilesApplication.DomainEventHandlers.cs b/src/UserProfilesApplication/UserProfilesApplication.DomainEventHandlers.cs new file mode 100644 index 00000000..6aa210e4 --- /dev/null +++ b/src/UserProfilesApplication/UserProfilesApplication.DomainEventHandlers.cs @@ -0,0 +1,26 @@ +using Application.Interfaces; +using Application.Resources.Shared; +using Common; +using Common.Extensions; +using Domain.Events.Shared.EndUsers; + +namespace UserProfilesApplication; + +partial class UserProfilesApplication +{ + public async Task> HandleEndUserRegisteredAsync(ICallerContext caller, Registered domainEvent, + CancellationToken cancellationToken) + { + var classification = domainEvent.Classification.ToEnumOrDefault(UserProfileClassification.Person); + var profile = + await CreateProfileAsync(caller, classification, domainEvent.RootId, domainEvent.Username, + domainEvent.UserProfile.FirstName, domainEvent.UserProfile.LastName, domainEvent.UserProfile.Timezone, + domainEvent.UserProfile.CountryCode, cancellationToken); + if (!profile.IsSuccessful) + { + return profile.Error; + } + + return Result.Ok; + } +} \ No newline at end of file diff --git a/src/UserProfilesApplication/UserProfilesApplication.cs b/src/UserProfilesApplication/UserProfilesApplication.cs index 4bd1dd5c..2d6e66b3 100644 --- a/src/UserProfilesApplication/UserProfilesApplication.cs +++ b/src/UserProfilesApplication/UserProfilesApplication.cs @@ -12,7 +12,7 @@ namespace UserProfilesApplication; -public class UserProfilesApplication : IUserProfilesApplication +public partial class UserProfilesApplication : IUserProfilesApplication { private readonly IIdentifierFactory _identifierFactory; private readonly IRecorder _recorder; @@ -26,114 +26,6 @@ public UserProfilesApplication(IRecorder recorder, IIdentifierFactory identifier _repository = repository; } - public async Task> CreateProfileAsync(ICallerContext caller, - UserProfileClassification classification, - string userId, string? emailAddress, string firstName, string? lastName, string? timezone, string? countryCode, - CancellationToken cancellationToken) - { - if (classification == UserProfileClassification.Person && emailAddress.HasNoValue()) - { - return Error.RuleViolation(Resources.UserProfilesApplication_PersonMustHaveEmailAddress); - } - - var retrievedById = await _repository.FindByUserIdAsync(userId.ToId(), cancellationToken); - if (!retrievedById.IsSuccessful) - { - return retrievedById.Error; - } - - if (retrievedById.Value.HasValue) - { - return Error.EntityExists(Resources.UserProfilesApplication_ProfileExistsForUser); - } - - if (classification == UserProfileClassification.Person && emailAddress.HasValue()) - { - var email = EmailAddress.Create(emailAddress); - if (!email.IsSuccessful) - { - return email.Error; - } - - var retrievedByEmail = await _repository.FindByEmailAddressAsync(email.Value, cancellationToken); - if (!retrievedByEmail.IsSuccessful) - { - return retrievedByEmail.Error; - } - - if (retrievedByEmail.Value.HasValue) - { - return Error.EntityExists(Resources.UserProfilesApplication_ProfileExistsForEmailAddress); - } - } - - var name = PersonName.Create(firstName, classification == UserProfileClassification.Person - ? lastName - : Optional.None); - if (!name.IsSuccessful) - { - return name.Error; - } - - var created = UserProfileRoot.Create(_recorder, _identifierFactory, - classification.ToEnumOrDefault(ProfileType.Person), - userId.ToId(), name.Value); - if (!created.IsSuccessful) - { - return created.Error; - } - - var profile = created.Value; - if (classification == UserProfileClassification.Person) - { - var email2 = EmailAddress.Create(emailAddress!); - if (!email2.IsSuccessful) - { - return email2.Error; - } - - var emailed = profile.SetEmailAddress(userId.ToId(), email2.Value); - if (!emailed.IsSuccessful) - { - return emailed.Error; - } - } - - var address = Address.Create(CountryCodes.FindOrDefault(countryCode)); - if (!address.IsSuccessful) - { - return address.Error; - } - - var contacted = profile.SetContactAddress(userId.ToId(), address.Value); - if (!contacted.IsSuccessful) - { - return contacted.Error; - } - - var tz = Timezone.Create(Timezones.FindOrDefault(timezone)); - if (!tz.IsSuccessful) - { - return tz.Error; - } - - var timezoned = profile.SetTimezone(userId.ToId(), tz.Value); - if (!timezoned.IsSuccessful) - { - return timezoned.Error; - } - - var saved = await _repository.SaveAsync(profile, cancellationToken); - if (!saved.IsSuccessful) - { - return saved.Error; - } - - _recorder.TraceInformation(caller.ToCall(), "Profile {Id} was created for user {userId}", profile.Id, userId); - - return saved.Value.ToProfile(); - } - public async Task, Error>> FindPersonByEmailAddressAsync(ICallerContext caller, string emailAddress, CancellationToken cancellationToken) { @@ -161,7 +53,8 @@ public async Task, Error>> FindPersonByEmailAddress public async Task> GetProfileAsync(ICallerContext caller, string userId, CancellationToken cancellationToken) { - if (userId != caller.CallerId) + if (!caller.IsServiceAccount + && userId != caller.CallerId) { return Error.ForbiddenAccess(); } @@ -359,6 +252,114 @@ public async Task, Error>> GetAllProfilesAsync(ICallerC .ConvertAll(profile => profile.ToProfile()) .ToList(); } + + private async Task> CreateProfileAsync(ICallerContext caller, + UserProfileClassification classification, string userId, string? emailAddress, string firstName, + string? lastName, string? timezone, string? countryCode, + CancellationToken cancellationToken) + { + if (classification == UserProfileClassification.Person && emailAddress.HasNoValue()) + { + return Error.RuleViolation(Resources.UserProfilesApplication_PersonMustHaveEmailAddress); + } + + var retrievedById = await _repository.FindByUserIdAsync(userId.ToId(), cancellationToken); + if (!retrievedById.IsSuccessful) + { + return retrievedById.Error; + } + + if (retrievedById.Value.HasValue) + { + return Error.EntityExists(Resources.UserProfilesApplication_ProfileExistsForUser); + } + + if (classification == UserProfileClassification.Person && emailAddress.HasValue()) + { + var email = EmailAddress.Create(emailAddress); + if (!email.IsSuccessful) + { + return email.Error; + } + + var retrievedByEmail = await _repository.FindByEmailAddressAsync(email.Value, cancellationToken); + if (!retrievedByEmail.IsSuccessful) + { + return retrievedByEmail.Error; + } + + if (retrievedByEmail.Value.HasValue) + { + return Error.EntityExists(Resources.UserProfilesApplication_ProfileExistsForEmailAddress); + } + } + + var name = PersonName.Create(firstName, classification == UserProfileClassification.Person + ? lastName + : Optional.None); + if (!name.IsSuccessful) + { + return name.Error; + } + + var created = UserProfileRoot.Create(_recorder, _identifierFactory, + classification.ToEnumOrDefault(ProfileType.Person), + userId.ToId(), name.Value); + if (!created.IsSuccessful) + { + return created.Error; + } + + var profile = created.Value; + if (classification == UserProfileClassification.Person) + { + var email2 = EmailAddress.Create(emailAddress!); + if (!email2.IsSuccessful) + { + return email2.Error; + } + + var emailed = profile.SetEmailAddress(userId.ToId(), email2.Value); + if (!emailed.IsSuccessful) + { + return emailed.Error; + } + } + + var address = Address.Create(CountryCodes.FindOrDefault(countryCode)); + if (!address.IsSuccessful) + { + return address.Error; + } + + var contacted = profile.SetContactAddress(userId.ToId(), address.Value); + if (!contacted.IsSuccessful) + { + return contacted.Error; + } + + var tz = Timezone.Create(Timezones.FindOrDefault(timezone)); + if (!tz.IsSuccessful) + { + return tz.Error; + } + + var timezoned = profile.SetTimezone(userId.ToId(), tz.Value); + if (!timezoned.IsSuccessful) + { + return timezoned.Error; + } + + var saved = await _repository.SaveAsync(profile, cancellationToken); + if (!saved.IsSuccessful) + { + return saved.Error; + } + + _recorder.TraceInformation(caller.ToCall(), "Profile {Id} was created for user {userId}", profile.Id, userId); + + return saved.Value.ToProfile(); + } } internal static class UserProfileConversionExtensions diff --git a/src/UserProfilesDomain/Events.cs b/src/UserProfilesDomain/Events.cs index d3a4de2c..552d22a8 100644 --- a/src/UserProfilesDomain/Events.cs +++ b/src/UserProfilesDomain/Events.cs @@ -8,10 +8,8 @@ public static class Events { public static ContactAddressChanged ContactAddressChanged(Identifier id, Identifier userId, Address address) { - return new ContactAddressChanged + return new ContactAddressChanged(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, Line1 = address.Line1, Line2 = address.Line2, @@ -25,10 +23,8 @@ public static ContactAddressChanged ContactAddressChanged(Identifier id, Identif public static Created Created(Identifier id, ProfileType type, Identifier userId, PersonName name) { - return new Created + return new Created(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, FirstName = name.FirstName, LastName = name.LastName.ValueOrDefault!, @@ -39,10 +35,8 @@ public static Created Created(Identifier id, ProfileType type, Identifier userId public static DisplayNameChanged DisplayNameChanged(Identifier id, Identifier userId, PersonDisplayName name) { - return new DisplayNameChanged + return new DisplayNameChanged(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, DisplayName = name }; @@ -50,10 +44,8 @@ public static DisplayNameChanged DisplayNameChanged(Identifier id, Identifier us public static EmailAddressChanged EmailAddressChanged(Identifier id, Identifier userId, EmailAddress emailAddress) { - return new EmailAddressChanged + return new EmailAddressChanged(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, EmailAddress = emailAddress }; @@ -61,10 +53,8 @@ public static EmailAddressChanged EmailAddressChanged(Identifier id, Identifier public static NameChanged NameChanged(Identifier id, Identifier userId, PersonName name) { - return new NameChanged + return new NameChanged(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, FirstName = name.FirstName, LastName = name.LastName.ValueOrDefault! @@ -73,10 +63,8 @@ public static NameChanged NameChanged(Identifier id, Identifier userId, PersonNa public static PhoneNumberChanged PhoneNumberChanged(Identifier id, Identifier userId, PhoneNumber number) { - return new PhoneNumberChanged + return new PhoneNumberChanged(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, Number = number }; @@ -84,10 +72,8 @@ public static PhoneNumberChanged PhoneNumberChanged(Identifier id, Identifier us public static TimezoneChanged TimezoneChanged(Identifier id, Identifier userId, Timezone timezone) { - return new TimezoneChanged + return new TimezoneChanged(id) { - RootId = id, - OccurredUtc = DateTime.UtcNow, UserId = userId, Timezone = timezone.Code.ToString() }; diff --git a/src/UserProfilesInfrastructure/ApplicationServices/UserProfilesInProcessServiceClient.cs b/src/UserProfilesInfrastructure/ApplicationServices/UserProfilesInProcessServiceClient.cs index 2c433894..9cbfd7d1 100644 --- a/src/UserProfilesInfrastructure/ApplicationServices/UserProfilesInProcessServiceClient.cs +++ b/src/UserProfilesInfrastructure/ApplicationServices/UserProfilesInProcessServiceClient.cs @@ -15,24 +15,6 @@ public UserProfilesInProcessServiceClient(IUserProfilesApplication userProfilesA _userProfilesApplication = userProfilesApplication; } - public async Task> CreateMachineProfilePrivateAsync(ICallerContext caller, - string machineId, string name, string? timezone, - string? countryCode, CancellationToken cancellationToken) - { - return await _userProfilesApplication.CreateProfileAsync(caller, UserProfileClassification.Machine, machineId, - null, name, - null, timezone, countryCode, cancellationToken); - } - - public async Task> CreatePersonProfilePrivateAsync(ICallerContext caller, - string personId, string emailAddress, string firstName, - string? lastName, string? timezone, string? countryCode, CancellationToken cancellationToken) - { - return await _userProfilesApplication.CreateProfileAsync(caller, UserProfileClassification.Person, personId, - emailAddress, - firstName, lastName, timezone, countryCode, cancellationToken); - } - public async Task, Error>> FindPersonByEmailAddressPrivateAsync(ICallerContext caller, string emailAddress, CancellationToken cancellationToken) { diff --git a/src/UserProfilesInfrastructure/Notifications/UserProfileNotificationConsumer.cs b/src/UserProfilesInfrastructure/Notifications/UserProfileNotificationConsumer.cs new file mode 100644 index 00000000..767a9875 --- /dev/null +++ b/src/UserProfilesInfrastructure/Notifications/UserProfileNotificationConsumer.cs @@ -0,0 +1,34 @@ +using Common; +using Domain.Events.Shared.EndUsers; +using Domain.Interfaces.Entities; +using Infrastructure.Eventing.Interfaces.Notifications; +using Infrastructure.Interfaces; +using UserProfilesApplication; + +namespace UserProfilesInfrastructure.Notifications; + +public class UserProfileNotificationConsumer : IDomainEventNotificationConsumer +{ + private readonly ICallerContextFactory _callerContextFactory; + private readonly IUserProfilesApplication _userProfilesApplication; + + public UserProfileNotificationConsumer(ICallerContextFactory callerContextFactory, + IUserProfilesApplication userProfilesApplication) + { + _callerContextFactory = callerContextFactory; + _userProfilesApplication = userProfilesApplication; + } + + public async Task> NotifyAsync(IDomainEvent domainEvent, CancellationToken cancellationToken) + { + switch (domainEvent) + { + case Registered registered: + return await _userProfilesApplication.HandleEndUserRegisteredAsync(_callerContextFactory.Create(), + registered, cancellationToken); + + default: + return Result.Ok; + } + } +} \ No newline at end of file diff --git a/src/UserProfilesInfrastructure/Notifications/UserProfileNotifier.cs b/src/UserProfilesInfrastructure/Notifications/UserProfileNotifier.cs new file mode 100644 index 00000000..7fc3c537 --- /dev/null +++ b/src/UserProfilesInfrastructure/Notifications/UserProfileNotifier.cs @@ -0,0 +1,18 @@ +using Infrastructure.Eventing.Common.Notifications; +using Infrastructure.Eventing.Interfaces.Notifications; +using UserProfilesDomain; + +namespace UserProfilesInfrastructure.Notifications; + +public class UserProfileNotifier : IEventNotificationRegistration +{ + public UserProfileNotifier(IEnumerable consumers) + { + DomainEventConsumers = consumers.ToList(); + } + + public List DomainEventConsumers { get; } + + public IIntegrationEventNotificationTranslator IntegrationEventTranslator => + new NoOpIntegrationEventNotificationTranslator(); +} \ No newline at end of file diff --git a/src/UserProfilesInfrastructure/UserProfilesModule.cs b/src/UserProfilesInfrastructure/UserProfilesModule.cs index 0557754a..56a23ac7 100644 --- a/src/UserProfilesInfrastructure/UserProfilesModule.cs +++ b/src/UserProfilesInfrastructure/UserProfilesModule.cs @@ -4,7 +4,9 @@ using Common; using Domain.Common.Identity; using Domain.Interfaces; +using Infrastructure.Eventing.Interfaces.Notifications; using Infrastructure.Hosting.Common.Extensions; +using Infrastructure.Interfaces; using Infrastructure.Persistence.Interfaces; using Infrastructure.Web.Hosting.Common; using Microsoft.AspNetCore.Builder; @@ -15,6 +17,7 @@ using UserProfilesDomain; using UserProfilesInfrastructure.Api.Profiles; using UserProfilesInfrastructure.ApplicationServices; +using UserProfilesInfrastructure.Notifications; using UserProfilesInfrastructure.Persistence; using UserProfilesInfrastructure.Persistence.ReadModels; @@ -51,10 +54,16 @@ public Action RegisterServices c.GetRequiredService(), c.GetRequiredService>(), c.GetRequiredServiceForPlatform())); - services.RegisterUnTenantedEventing( + services + .AddPerHttpRequest(c => + new UserProfileNotificationConsumer(c.GetRequiredService(), + c.GetRequiredService())); + services.RegisterUnTenantedEventing( c => new UserProfileProjection(c.GetRequiredService(), c.GetRequiredService(), - c.GetRequiredServiceForPlatform())); + c.GetRequiredServiceForPlatform()), + c => new UserProfileNotifier(c + .GetRequiredService>())); services.AddSingleton(); };