From 91e8ac43239eabb60c22003843787b022e6d9734 Mon Sep 17 00:00:00 2001 From: Jezz Santos Date: Wed, 14 Feb 2024 12:26:07 +1300 Subject: [PATCH] Created FeatureFlagging services, APIs and source generator, and Flagsmith adapter, with stubs, and docs. #4 --- README.md | 14 +- README_DERIVATIVE.md | 8 + .../0080-ports-and-adapters.md | 288 +++++++++++++++++- .../0120-feature-flagging.md | 59 ++++ docs/design-principles/README.md | 5 +- docs/images/Ports-And-Adapters.png | Bin 0 -> 138409 bytes docs/images/Sources.pptx | Bin 738084 -> 901265 bytes src/.gitignore | 7 +- .../inspectionProfiles/Project_Default.xml | 8 + .../HostExtensions.cs | 2 + .../FeatureFlagsApplicationSpec.cs | 85 ++++++ .../FeatureFlagsApplication.cs | 20 +- .../IFeatureFlagsApplication.cs | 17 ++ .../EmailDeliverRootSpec.cs | 2 +- .../FeatureFlagsApiSpec.cs | 62 ++++ ...eatureFlagForCallerRequestValidatorSpec.cs | 40 +++ .../GetFeatureFlagRequestValidatorSpec.cs | 90 ++++++ .../AncillaryModule.cs | 1 + .../Api/FeatureFlags/FeatureFlagsApi.cs | 48 +++ ...GetFeatureFlagForCallerRequestValidator.cs | 14 + .../GetFeatureFlagRequestValidator.cs | 24 ++ .../Resources.Designer.cs | 27 ++ src/AncillaryInfrastructure/Resources.resx | 9 + src/ApiHost1/Properties/launchSettings.json | 6 +- src/ApiHost1/appsettings.json | 4 + .../CallerContextExtensionsSpec.cs | 2 +- .../Extensions/FeatureFlagExtensionsSpec.cs | 116 +++++++ .../Extensions/FeatureFlagsExtensions.cs | 47 +++ .../AzureFunctions.Api.WorkerHost.csproj | 1 + .../HostExtensions.cs | 2 + .../Extensions/StringExtensionsSpec.cs | 112 +++++++ src/Common/Common.csproj | 22 +- src/Common/Extensions/StringExtensions.cs | 128 +++++++- src/Common/FeatureFlags/FeatureFlag.cs | 11 + .../FeatureFlags/FeatureFlags.Designer.cs | 71 +++++ src/Common/FeatureFlags/FeatureFlags.resx | 29 ++ src/Common/FeatureFlags/Flag.cs | 24 ++ src/Common/FeatureFlags/IFeatureFlags.cs | 35 +++ .../FeatureFlags/Flag.g.cs | 11 + .../Flag.g.cs | 11 + .../EmptyFeatureFlags.cs | 41 +++ .../Infrastructure.Hosting.Common.csproj | 4 - .../FlagsmithHttpServiceClientSpec.cs | 244 +++++++++++++++ ...rastructure.Shared.IntegrationTests.csproj | 27 ++ .../TestHttpClientFactory.cs | 9 + .../appsettings.Testing.json | 26 ++ .../FlagsmithHttpServiceClient.TestingOnly.cs | 137 +++++++++ .../External/FlagsmithHttpServiceClient.cs | 211 +++++++++++++ .../OAuth2HttpServiceClient.cs | 2 +- .../Infrastructure.Shared.csproj | 27 +- .../Resources.Designer.cs | 9 + src/Infrastructure.Shared/Resources.resx | 3 + .../Extensions/HttpRequestExtensionsSpec.cs | 14 + .../Extensions/HttpRequestExtensions.cs | 11 +- .../Extensions/RequestExtensions.cs | 2 +- .../WebServiceAttribute.cs | 21 ++ ...thCreateEdgeIdentityFeatureStateRequest.cs | 21 ++ .../FlagsmithCreateEdgeIdentityRequest.cs | 16 + .../FlagsmithCreateEdgeIdentityResponse.cs | 11 + .../FlagsmithCreateFeatureRequest.cs | 12 + .../FlagsmithCreateFeatureResponse.cs | 13 + .../FlagsmithCreateFeatureStateRequest.cs | 16 + .../FlagsmithCreateFeatureStateResponse.cs | 7 + .../FlagsmithCreateIdentityRequest.cs | 14 + .../FlagsmithCreateIdentityResponse.cs | 22 ++ .../FlagsmithDeleteEdgeIdentitiesRequest.cs | 11 + .../FlagsmithDeleteFeatureRequest.cs | 11 + .../FlagsmithGetEdgeIdentitiesRequest.cs | 9 + .../FlagsmithGetEdgeIdentitiesResponse.cs | 18 ++ .../FlagsmithGetEnvironmentFlagsRequest.cs | 10 + .../FlagsmithGetEnvironmentFlagsResponse.cs | 34 +++ .../FlagsmithGetFeatureStatesRequest.cs | 12 + .../FlagsmithGetFeatureStatesResponse.cs | 18 ++ .../Flagsmith/FlagsmithGetFeaturesRequest.cs | 9 + .../Flagsmith/FlagsmithGetFeaturesResponse.cs | 9 + .../ExchangeOAuth2CodeForTokensRequest.cs | 2 +- .../ExchangeOAuth2CodeForTokensResponse.cs | 2 +- .../Ancillary/GetAllFeatureFlagsRequest.cs | 9 + .../Ancillary/GetAllFeatureFlagsResponse.cs | 9 + .../GetFeatureFlagForCallerRequest.cs | 9 + .../Ancillary/GetFeatureFlagRequest.cs | 14 + .../Ancillary/GetFeatureFlagResponse.cs | 9 + .../GetAllFeatureFlagsRequest.cs | 8 + .../GetAllFeatureFlagsResponse.cs | 9 + .../GetFeatureFlagForCallerRequest.cs | 9 + .../GetFeatureFlagResponse.cs | 9 + .../Clients/ApiServiceClient.cs | 16 +- .../Clients/InterHostServiceClient.cs | 16 +- .../Clients/JsonClient.cs | 9 +- .../Extensions/HostExtensions.cs | 3 + .../Infrastructure.Web.Hosting.Common.csproj | 1 + .../Clients/IServiceClient.cs | 16 +- .../FeatureFlagsApiSpec.cs | 75 +++++ ...ucture.Web.Website.IntegrationTests.csproj | 1 + .../WebsiteTestingExtensions.cs | 1 + ...eatureFlagForCallerRequestValidatorSpec.cs | 41 +++ .../FeatureFlagsApplicationSpec.cs | 81 +++++ .../Stubs/StubServiceClient.cs | 14 +- .../ExternalApiSpec.cs | 120 ++++++++ .../IntegrationTesting.WebApi.Common.csproj | 1 + .../Stubs/StubFeatureFlags.cs | 49 +++ .../WebApiSpec.cs | 2 + src/SaaStack.sln | 27 ++ src/SaaStack.sln.DotSettings | 9 + .../Api/StubFlagsmithApi.cs | 62 ++++ src/TestingStubApiHost/appsettings.json | 2 +- .../FeatureFlagGeneratorSpec.cs | 97 ++++++ .../Tools.Generators.Common.UnitTests.csproj | 21 ++ .../FeatureFlagGenerator.cs | 95 ++++++ src/Tools.Generators.Common/README.md | 43 +++ .../Tools.Generators.Common.csproj | 32 ++ .../README.md | 4 +- ...ls.Generators.Web.Api.Authorization.csproj | 2 +- .../MinimalApiMediatRGeneratorSpec.cs | 90 +++++- .../WebApiAssemblyVisitorSpec.cs | 54 +++- .../MinimalApiMediatRGenerator.cs | 6 +- src/Tools.Generators.Web.Api/README.md | 4 +- .../Tools.Generators.Web.Api.csproj | 5 +- .../WebApiAssemblyVisitor.cs | 25 +- .../Api/FeatureFlags/FeatureFlagsApi.cs | 39 +++ ...GetFeatureFlagForCallerRequestValidator.cs | 14 + src/WebsiteHost/Api/Recording/RecordingApi.cs | 33 +- .../Application/FeatureFlagsApplication.cs | 54 ++++ .../Application/IFeatureFlagsApplication.cs | 14 + .../Application/IRecordingApplication.cs | 16 +- .../Application/RecordingApplication.cs | 39 ++- src/WebsiteHost/BackEndForFrontEndModule.cs | 1 + src/WebsiteHost/Resources.Designer.cs | 9 + src/WebsiteHost/Resources.resx | 3 + 129 files changed, 3739 insertions(+), 119 deletions(-) create mode 100644 docs/design-principles/0120-feature-flagging.md create mode 100644 docs/images/Ports-And-Adapters.png create mode 100644 src/AncillaryApplication.UnitTests/FeatureFlagsApplicationSpec.cs create mode 100644 src/AncillaryApplication/IFeatureFlagsApplication.cs create mode 100644 src/AncillaryInfrastructure.IntegrationTests/FeatureFlagsApiSpec.cs create mode 100644 src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs create mode 100644 src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagRequestValidatorSpec.cs create mode 100644 src/AncillaryInfrastructure/Api/FeatureFlags/FeatureFlagsApi.cs create mode 100644 src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs create mode 100644 src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagRequestValidator.cs rename src/Application.Common.UnitTests/{ => Extensions}/CallerContextExtensionsSpec.cs (96%) create mode 100644 src/Application.Common.UnitTests/Extensions/FeatureFlagExtensionsSpec.cs create mode 100644 src/Application.Common/Extensions/FeatureFlagsExtensions.cs create mode 100644 src/Common/FeatureFlags/FeatureFlag.cs create mode 100644 src/Common/FeatureFlags/FeatureFlags.Designer.cs create mode 100644 src/Common/FeatureFlags/FeatureFlags.resx create mode 100644 src/Common/FeatureFlags/Flag.cs create mode 100644 src/Common/FeatureFlags/IFeatureFlags.cs create mode 100644 src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/FeatureFlags/Flag.g.cs create mode 100644 src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/Flag.g.cs create mode 100644 src/Infrastructure.Hosting.Common/EmptyFeatureFlags.cs create mode 100644 src/Infrastructure.Shared.IntegrationTests/ApplicationServices/External/FlagsmithHttpServiceClientSpec.cs create mode 100644 src/Infrastructure.Shared.IntegrationTests/Infrastructure.Shared.IntegrationTests.csproj create mode 100644 src/Infrastructure.Shared.IntegrationTests/TestHttpClientFactory.cs create mode 100644 src/Infrastructure.Shared.IntegrationTests/appsettings.Testing.json create mode 100644 src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.TestingOnly.cs create mode 100644 src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.cs create mode 100644 src/Infrastructure.Web.Api.Interfaces/WebServiceAttribute.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityFeatureStateRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteEdgeIdentitiesRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteFeatureRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesResponse.cs rename src/Infrastructure.Web.Api.Operations.Shared/3rdParties/{ => OAuth2}/ExchangeOAuth2CodeForTokensRequest.cs (90%) rename src/Infrastructure.Web.Api.Operations.Shared/3rdParties/{ => OAuth2}/ExchangeOAuth2CodeForTokensResponse.cs (86%) create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagForCallerRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsResponse.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagForCallerRequest.cs create mode 100644 src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagResponse.cs create mode 100644 src/Infrastructure.Web.Website.IntegrationTests/FeatureFlagsApiSpec.cs create mode 100644 src/Infrastructure.Web.Website.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs create mode 100644 src/Infrastructure.Web.Website.UnitTests/Application/FeatureFlagsApplicationSpec.cs create mode 100644 src/IntegrationTesting.WebApi.Common/ExternalApiSpec.cs create mode 100644 src/IntegrationTesting.WebApi.Common/Stubs/StubFeatureFlags.cs create mode 100644 src/TestingStubApiHost/Api/StubFlagsmithApi.cs create mode 100644 src/Tools.Generators.Common.UnitTests/FeatureFlagGeneratorSpec.cs create mode 100644 src/Tools.Generators.Common.UnitTests/Tools.Generators.Common.UnitTests.csproj create mode 100644 src/Tools.Generators.Common/FeatureFlagGenerator.cs create mode 100644 src/Tools.Generators.Common/README.md create mode 100644 src/Tools.Generators.Common/Tools.Generators.Common.csproj create mode 100644 src/WebsiteHost/Api/FeatureFlags/FeatureFlagsApi.cs create mode 100644 src/WebsiteHost/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs create mode 100644 src/WebsiteHost/Application/FeatureFlagsApplication.cs create mode 100644 src/WebsiteHost/Application/IFeatureFlagsApplication.cs diff --git a/README.md b/README.md index 7ab4a283..65fee2e7 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,18 @@ # SaaStack -Are you about to build a new SaaS product from scratch and do that on .NET? +Are you about to build a new SaaS product from scratch? On .NET? -Then, start with SaaStack. +Then, try starting with SaaStack codebase template. -It is a complete "codebase template" for building real-world, fully featured SaaS web products. +It is a complete template for building real-world, fully featured SaaS web products. Ready to build, test, and deploy into a cloud provider of your choice (e.g., Azure, AWS, Google Cloud, etc.) -> Don't spend months building all this stuff from scratch. You and your team don't need to. We've done all that for you already; just take a look, see hat is there and take it from here. You can always change it the way you like it as you proceed, you are not locked into anyone else framework. +> Don't spend months building all this stuff from scratch. You and your team don't need to. We've done all that for you already; just take a look, see what is already there and take it from here. You can always change it the way you like it as you proceed, you are not locked into anyone else's framework. > > This is not some code sample like those you would download to learn a new technology or see in demos online. This is way more comprehensive, way more contextualized, and way more realistic about the complexities you are going to encounter in reality. -> This template contains a partial (but fully functional) SaaS product that you can deploy from day one and start building your product on. But it is not yet complete. That part is up to you. +> This template contains a partial (but fully functional) SaaS product that you can deploy from day one and start building your product on. But it is not yet complete. That next part is up to you. The codebase demonstrates common architectural styles that you are going to need in your product in the long run, such as: @@ -42,7 +42,7 @@ or if you prefer AWS: ## Who is it for? -This starter template is NOT for everyone, nor for EVERY software project, nor for EVERY skill level. +This starter template is NOT for everyone, nor for EVERY software project, nor for EVERY skill level. We need to say that because all software products are different, there is not one silver bullet for all of them. * The people using this template must have some experience applying "first principles" of building new software products from scratch because it is a starter template that can (and should) be modified to suit your context. It is a far better starting point than building everything from scratch again. @@ -126,7 +126,7 @@ The starter template also takes care of these specific kinds of things: * It integrates product usage metrics to monitor and measure the actual usage of your product (e.g., MixPanel, Google Analytics, Application Insights, Amazon XRay, etc.) * It integrates crash analytics and structured logging so you can plug in your own preferred monitoring (e.g., Application Insights, CloudWatch, Sentry.io, etc.). * It uses dependency injection extensively so that all modules and components remain testable and configurable. - * It defines standard and unified configuration patterns (e.g., using appsettings.json) to load tenanted or non-tenanted runtime settings. + * It defines standard and unified configuration patterns (e.g., using `appsettings.json`) to load tenanted or non-tenanted runtime settings. * Application * Supports one or more applications, agnostic to infrastructure interop (i.e., allows you to expose each application as a REST API (default) or as a reliable Queue, or any other kind of infrastructure) * Supports transaction scripts + anemic domain model or Domain Driven Design diff --git a/README_DERIVATIVE.md b/README_DERIVATIVE.md index 9a728be9..15da0bd1 100644 --- a/README_DERIVATIVE.md +++ b/README_DERIVATIVE.md @@ -106,6 +106,14 @@ Now, test that LocalStack works by running: `localstack start` > When testing, Docker will need to be running for LocalStack to be used +### External Adapter Testing + +> You only need to perform this step once, prior to running any of the `Integration.External` tests against 3rd party adapters (e.g., Flagsmith, Twillio, etc) + +In the `Infrastructure.Shared.IntegrationTests` project, create a new file called `appsettings.Testing.local.json` and fill out the empty placeholders you see in `appsettings.TestingOnly.json` with values from service accounts that you have created for testing those 3rd party services. + +> DO NOT add this file to source control! + # Build & Deploy When pushed, all branches will be built and tested with GitHub actions diff --git a/docs/design-principles/0080-ports-and-adapters.md b/docs/design-principles/0080-ports-and-adapters.md index f4b0559f..1c3d757f 100644 --- a/docs/design-principles/0080-ports-and-adapters.md +++ b/docs/design-principles/0080-ports-and-adapters.md @@ -1,5 +1,291 @@ # Ports and Adapters +The underlying architectural style in this codebase is the Ports and Adapters. This means that the application is designed to drive and be driven by the domain model, and the domain model is not dependent on any external infrastructure. + +![Ports and Adapters](../images/Ports-And-Adapters.png) + +In this way, we can re-focus on the core domain model and be protected by changes in infrastructure. + +Possibly the most powerful effect of this approach is that any data, any service, and interaction that is needed by the core domain is accessible by a domain specific "port" and can be implemented by one or more "adapters" connecting to other components or external systems. For the developer, this liberates them to tackle a complex interaction in testable steps. + +This is achieved by using the [Adapter Pattern](https://en.wikipedia.org/wiki/Adapter_pattern) to connect the domain model to the external infrastructure. + +In general, this de-coupling pattern can also be applied in any layer of the code, but it is particularly useful in the "Application Layer" and in the "Infrastructure Layers". The pattern can also be used in composing infrastructure components together to offer several abstraction layers, between the logical and physical layers. + +> In other words, you don't need to go from a single "port" to a single "adapter" representing physical infrastructure (like a database). Your "port" can be implemented by an "adapter" that exposes a lower level "port" that can be implemented by a lower level "adapter". + +A "port" defines the data and behavior required by the consumer of it. Thus the consumer is always protected from changes in any implementation of the "port". +> This pattern is also known as the "[Plug-in Pattern](https://en.wikipedia.org/wiki/Plug-in_(computing))", which is common in reusable libraries or frameworks that offer extensibility points for those using them. + +A "port," in code is simply defined in an interface. +> It is designed to be as small as possible, and as specific as possible, adhering to the [ISP principle](https://en.wikipedia.org/wiki/SOLID). + +A "service" that implements the "port", is known as an "adapter" + +An "adapter" is realized as a concrete class, that simply implements the "port" interface. +> An adapter, in code, rarely ever uses the term "adapter" as part of its name or identifier. Rather, it is usually named after the behavior it provides, the 3rd party library it wraps, or the 3rd party service it integrates with. + +Whenever a port is required to be used by a component, it declares the port in its constructor, and dependency injection is used to inject a real adapter into the component at runtime (depending on what is currently registered in the container at that time). + +An "adapter" class typically implements a specific kind of behavior that is self-describing in the class, or it will wrap a third-party library to implement this behavior, or it will wrap a 3rd party SDK that relays the information to a remote 3rd party system. In general, these "adapters" facilitate access to infrastructure components, such as databases, message queues, or machine infrastructure like clocks, disks, encryption, configuration, etc, or access to remote 3rd party services. Any I/O of any kind. Sometimes, they provide access to other subdomains and deployed modules in the same codebase. Essentially they help the developer separate multiple concerns into services that can be independently tested and developed. + +This concrete "adapter" is then registered in the dependency injection container at runtime, for injection to where ever the port is required. +> In this way, the consumer of the port is completely de-coupled from the implementation of the port, and the implementation of the port is completely de-coupled from the consumer of the port. + +What this also enables is that the code for the port and code for the adapter(s) can be kept in different libraries, further reducing the coupling of code in reusable libraries in the code. This leads to far better maintainability and portability of shared libraries in the code. Which is a key enabler for modular monoliths, when they have to be split up. + ## Design Principles -## Implementation \ No newline at end of file +* We want to maintain high levels of de-coupling for performing any actions outside subdomain aggregates in the "Domain Layer". This means that access to any data outside of a subdomain requires the use of a "port" to obtain it, which provides data that can be presented to the aggregate in the subdomain. This is the primary job of the "Application Layer". +* We want to be able to swap out the implementation of any "adapter" (something that provides/processes its own data) without changing the domain model or requiring a change in it. +* We want to be able to unit test the domain model in isolation without requiring any external infrastructure. (A domain model should only require data and, in some rare cases, require "domain services" which can be mocked in order to be tested). +* We want to be able to test the real infrastructural "adapters" in isolation also, without needing to use the domain model. +* If the real infrastructure "adapters" require external 3rd party services to run properly, then we want to be able to test them in isolation as well. (using testing-only configurations) +* Given that we can reliably unit test the domain model independently, and we can reliably unit test the infrastructure components independently and given that we can integration test the domain model with "stubbed" infrastructure "adapters" (that can "fake" the behavior of real infrastructure "adapter"), then we can then be very confident that the system will work as expected when it is deployed with real infrastructure "adapters" in staging/production environments. In fact, if this is the case, then the only unknown should be whether the staging/production configuration used by the real infrastructure "adapter" is valid or not in the staging/production environments. If it is valid, then the system should work in staging/production as designed. +* When we run the code locally in the local environment (for manual testing or demos, etc.) we want to avoid having to install any infrastructural components that cannot be swapped out for adapters that run in memory or on disk on the local machine. For example, we don't want to install databases, or data servers of any kind. +* When we run the code in automated testing we also do not have to install any infrastructural components, especially if they require network communication. Local automated testing and automated testing in CI should be offline, and trivial to configure. + +## Implementation + +### Application Services + +Many of the ports and adapters in the codebase are to be found in the "services" used by the classes in the "Application Layer" (not exclusively). + +> They are often known more commonly as "services" borrowed from other, more traditional architectures. +> +> There are examples of the same mechanism of ports and adapters all over the "Infrastructure Layer" too. +> +> Ports and adapters (as a pattern) are implemented at "Domain Services" in the "Domain Layer" + +The main class in the "Application Layer" of any subdomain (e.g., `CarsApplication`) defines new "ports" (e.g., `ICarRepository`) to either access data from other components or to delegate certain operations to other components. Ports are also the prescribed way to communicate with other subdomains. + +Typically, the main "Application Layer" class consumes (or "drives") these "ports". + +> This is an important mechanism to separate concerns of the classes in the application layer, whose primary purpose is to obtain data for the aggregate of the "Domain Layer" to operate. + +Conversely, the classes in the "Application Layer" of a given module (e.g., `CarsApplication`) are, in fact, "adapters" themselves. They implement the declared "port" (e.g., `ICarsApplication`) that is "driven" by the upstream API class that consumes this "port". + +#### Unit Testing + +Ports are never unit tested, as they are interfaces. + +Most "adapters" are unit tested. Or if not, they are covered in integration tests. Sometimes both are necessarily used. + +There are some notable exceptions to that unit testing rule: + +* API "service" classes (define the API) - they implement the two-way adapter to the internet. They are typically not unit tested because they simply delegate calls to the underlying domain-specific "Application Layer". Unit tests here would not achieve much that API integration tests wouldn't verify. In some rare cases, where there is less-trivial processing or mapping of data, there may be unit tests to cover that logic. +* Data "Repositories" (used by the "Application Layer") - they implement a two-way adapter to some kind of data store. They are typically not unit tested because they simply delegate calls to underlying generic "ports". Unit testing here would not achieve much that integration tests wouldn't verify. +* Inter-module "Service Clients" (used by the "Application Layer") - like repositories above, they implement two-way adapters to another subdomain. They are typically not unit tested because they simply delegate calls to underlying generic "ports" (e.g., an application interface like `ICarsApplication` or an HTTP service client). Unit testing here would not achieve much that integration tests wouldn't verify. + +> Repositories are only referred to as "repositories" for historical reason; in general,l they are just a specialization of a "service". + +### 3rd Party Services + +Adapters to 3rd party remote systems (a.k.a "3rd party integrations") are a special kind of "adapter" requiring additional and special treatment in the codebase, so that they are properly tested and configured correctly for use in local environments, as well as in staging/production environments. + +The main difference between them is that they: + +1. Typically require the creation of accounts in the 3rd party online system (e.g., register an account with a 3rd party service and obtain an API key, a client ID, a client secret, etc). In these cases, they require separate accounts for testing-only and separate accounts for staging/production use. Testing-only accounts can never lead observers to compromising staging/production accounts. Some 3rd party providers mitigate this with sandbox environments, but not all provide that service. +2. They often offer the use of a 3rd party SDK, a 3rd party library, or a 3rd party service to communicate with the 3rd party system. These can be very useful sometimes, depending on their sophistication (particularly with caching, retry policies, etc), but they must be configurable to point to local "stubbed" versions of them in order to be used in local development environments. +3. They require connection configuration (and often secrets) to access the 3rd party system, which often differs between local, staging, and production environments. Details for staging/production environments can never be hard-coded into the codebase, and must be configurable at deployment time. +4. They require us to build one or more "stub" implementations of the 3rd party system so that they can be used in local development and in automated testing without requiring access to the real 3rd party system. + +In practice, to build one of these 3rd party adapters, you need to take extra time and care to provide (at least) these six things: + +1. An "adapter" to the real 3rd party system. +2. A number of accounts with the 3rd party +3. Integration tests that test the adapter against the real 3rd party system, using real configuration. +4. Register the adapter in the dependency injection container. +5. A stub adapter that can be used in local/CI automated testing. +6. A stub API that can be used in local development. + +#### Adapter + +Most external adapters for 3rd party integrations, that you build, should be built and maintained in the `Infrastructure.Shared` project in a folder called: `ApplicationServices/External`. +> This single project would be the only project that would take a dependency on any 3rd party NuGet packages required by these adapters, which should be kept to a minimum. + +Your adapter will likely act as an HTTPS "Service Client" to a remote 3rd party service (in the cloud or deployed in your cloud, a.k.a on-premise). Thus, the name of your adapter is likely to follow the naming pattern: `VendorHttpServiceClient`. + +Your adapter can use the vendor-provided SDK library (from NuGet), but consider these challenges: + +1. If the adapter has significant complexity to it, and/or your adapter has behavior that you feel should be unit tested, then using the SDK library will make it very hard to unit test the adapter since you cannot be accessing a remote system during unit testing. +2. If this adapter is going to be used in local development (i.e., it is injected into the DI container in local development), then the SDK will be required to be configured to direct all its HTTP traffic to a stub API that you must provide (refer to step 6 above). + +If you want to unit test your adapter (not always necessary), then an easy technique to use is to wrap the SDK code inside another port (e.g. , `IVendorServiceClient`) and then implement that adapter using the vendor-provided SDK, with an internal constructor used only for testing. This will allow you to unit test your original adapter, by injecting a `new Mock()`. + +For example, + +```c# +public class VendorHttpServiceClient : IMyPort +{ + private readonly IVendorServiceClient _serviceClient; + + // used to inject into DI container + public MyAdapter(IRecorder recorder): this(recorder, new VendorServiceClient() + { + } + + // used only for unit testing + internal MyAdapter(IRecorder recorder, IVendorServiceClient serviceClient) + { + _serviceClient = serviceClient; + } + + // remaining methods of the IMyPort interface +} +``` + +If you are plugging in your adapter for local development and automated testing, then you will be providing a stub API (as per step 6 above). In this case, the vendor SDK will need to be able to send its HTTP requests to another URL other than the one in the cloud where their service is hosted (e.g., `https://api.vendor.com`). The SDK must support a way for you to change that `BaseUrl`. + +If not, you have very few good choices other than to: + +1. Inject a different implementation of the `IVendorServiceClient` above. +2. Forgo using the vendor SDK altogether, and instead use the `ApiServiceClient` and send the HTTP requests yourself to the remote vendor API. This is more work, but it is the only way to ensure that the adapter can be used in local development and automated testing properly. + +#### Accounts with 3rd party + +In general, most 3rd party vendor-provided services will require you to register and create accounts with them to gain access to their public APIs. +> Note: some of the services require being paid for, which is another hurdle. + +For most of these integrations, you are going to need to create at least two accounts with the 3rd party: + +1. One account called "testingonly" that will be used for local development and automated testing. +2. One/more account(s) for "staging/production" that is used for staging and production environments. + +> Some vendors provide sandboxes for testing, which can be used in place of the "testingonly" account. This is a great feature, but not all vendors provide this. + +Regardless, the credentials, access, and configurations between these accounts must be managed separately. + +You must never expose staging/production credentials or configuration outside your organization. This is a serious security risk. + +You can, however, expose testing-only credentials and configuration inside your organization, but only if they cannot lead to those used in staging/production environments. Separate secured and protected accounts are strongly recommended. + +> In this SaaStack codebase template, the contributors have registered testing-only accounts with various vendors, and we have hard-coded some of those credentials in the various `appsettings.Testing.json` files of the various adapter integration testing projects, in the codebase. This is a security risk, but it is acceptable for the purposes of this template since this cannot lead to any staging/production environments. These accounts can be compromised with little exposure. In a real derivative project, you are using, if this code was exposed outside your organization (for example, in open source), you should not do this. If the codebase is not exposed outside your own organization, there is also some risk in exposing these to those with access to your code. You might consider using secrets files, environment variables, a secret manager, a key vault, or a configuration service to manage this sensitive configuration. + +#### Integration Testing + +Integration tests are REQUIRED to test the real adapter against the real 3rd party vendor systems, using real configuration, to ensure that the adapter works as designed. + +These integration tests are different than others, in many ways: + +1. They are of a different category called "Integration.External" +2. They should not be executed frequently by the team, like other integration tests are, since they are testing against real 3rd party systems, can be slow, can be rate limited, and can incur costs to your organization (depending on the 3rd party system, and pricing). +3. They should be executed infrequently in CI builds, perhaps once a week/month or whenever the code in the adapters is changed. +4. They can fail from time to time, depending on how well-managed the 3rd party vendor is at maintaining their systems. When these tests do break, it's a pretty clear indicator that the 3rd party service has changed from what it used to be, and your team needs to know ASAP. Or perhaps your adapters are now broken and need to be fixed. + +#### Configuration + +The adapter must be configurable at runtime, so that it can be used in local development, automated testing, and in staging/production environments. + +This configuration is likely to be kept in `appsettings.json` files in each of the host projects that use the adapter (e.g., `ApiHost1.csproj`). + +Configuration for each of these adapters is done under the `ApplicationServices` section of the `appsettings.json` file, usually under a key named after the vendor. + +For example, + + ```json + { + "ApplicationServices": { + "Vendor": { + "BaseUrl": "https://api.vendor.com", + "ClientId": "client-id", + "ClientSecret": "client-secret" + } + } + } + ``` + +Now, from a security perspective and a testing perspective, you do not want to define any real configuration settings here since that would expose them to anyone who can see the code. + +In automated "External" Integration testing (as described above), the configuration used there to talk to a real online service, is kept in `appsettings.Testing.json` (and in `appsettings.testing.local.json`) in the testing project. + +In automated "API" Integration testing, your adapter is going to be swapped out for a "stubbed" adapter that is injected at testing time, using overrides in the integration testing project so that your adapter is not used at all. + +Configuration in the host project is there for running the adapter in local development and in staging/production environments. + +For staging/production environments, your automated deployment process should be substituting/replacing the configuration in `appsettings.json` just before deployment time with the correct configuration to real 3rd party systems. + +But for local development (manual debugging and testing), you need settings here that can be used in your adapter, to talk to a stub API (see below). This means that you want predominantly empty placeholder or testing entries in `appsettings.json` that have no sensitive values. + +> Remember: to update all staging/production configurations to your CI/CD systems so that you don't forget those settings on the next deployment of code. + +#### Register in Dependency Injection Container + +The adapter must be registered in the dependency injection container, so that it can be injected into consumer classes that need it at runtime. That can be done in one of two places: + +1. In the `HostExtensions.cs` file of the `Infrastructure.Web.Hosting.Common` project, in the `ConfigureApiHost` method. +2. In the respective `ISubDomainModule` class of the subdomain that uses the adapter, in the `ISubDomainModule.RegisterServices` method. + +Furthermore, you can use compiler compilation directives to register this adapter in different build configurations or different hosting environments. + +For example, if you want it only to be used in local development and automated testing, or never in either of those, you can use the `#if TESTINGONLY` directive. + +For example, if you want it only to be used in an AZURE deployment, not an AWS deployment, or never in either of those, you can use the `#if HOSTEDONAZURE || HOSTEDONAWS` directives. + +#### Stub Adapter + +In automated "API" Integration testing, we do NOT want to be using the real adapter at all. Instead, we want to use a programmable "stub," so we can control its behavior, and we don't want to use online access to real systems across the network. + +Depending on which subdomain the adapter is used in, you will need to provide a stub adapter that can be used in automated "API" Integration testing, rather than using the real adapter at all. + +This "stub" adapter replaces your adapter, and provides a fake implementation of the real adapter. Often providing limited data or default behavior, enough to test the API under test. + +You have two choices depending on the scope of your adapter. + +1. If your adapter is used by all subdomains and all hosts (i.e., it is used everywhere), then you can create a "stub" adapter in the `IntegrationTesting.Web.Api.Common` project in the `Stubs` folder, and you can inject it into the `WebApiSetup` in the `ConfigureWebHost` method along with the other global adapters. +2. If your adapter is only used in one (or more) specific subdomains, then you can create the "stub" adapter in the integration testing project of that subdomain (i.e., the `SubdomainInfrastructure.IntegrationTests` project), in a folder called `Stubs`, and you can inject that sub into the `OverrideDependencies` method of your test class. + +For example, if you take a look at the `StubFeatureFlags` in the `IntegrationTesting.Web.Api.Common` project, you can see how it is injected into the `WebApiSetup` in the `ConfigureWebHost` method. + +For example, if you look at the `StubEmailDeliveryService` in the `AncillaryInfrastructure.IntegrationTests` project, you can see how it is injected into the `OverrideDependencies` method of the `EmailsApiSpec` class. + +#### Stub API + +In local development, we DO want to be using the real adapter, but we want to have a programmable 3rd party "stub" API so that we can control its behavior, and we don't want to use online access to real systems across the network. + +This "stub" API stands in for the real 3rd party API, and the real adapter communicates with this "stub" API. + +> This additional step is only necessary for adapters that access 3rd party online systems. If your adapter does not access a 3rd party online system, then you do not need to provide a stub API. + +To make this work, we need the configuration defined in the `appsettings.json` file of the Host project to point to the "stub" API instead of pointing to the real 3rd party system. This API will always be hosted by the `TestingStubApiHost` project at `https://localhost:5656`. + +For example, the `BaseUrl` configuration setting in the `appsettings.json` file of the Host project for your adapter could be set to `https://localhost:5656/vendor`. + +```json +{ + "ApplicationServices": { + "Vendor": { + "BaseUrl": "https://localhost:5656/vendor/" + } + } +} +``` + +In the `TestingStubApiHost` project, in the `Api` folder: + +1. Create a class that derives from `StubApiBase` (e.g., `StubVendorApi`). +2. Consider applying a `WebServiceAttribute` to define a prefix for this API (e.g. `[WebService("/vendor")]`) to separate this API from the other vendors that will also be hosted at `https://localhost:5656`. +3. Implement the HTTP endpoints that your adapter uses, and provide empty or default responses that your adapter expects to receive. (same as the way we implement any endpoints) +4. The Request types, Response types (and all complex data types that are used in the request and response) should all be defined in the `Infrastructure.Web.Api.Operations.Shared` project in a subfolder of the `3rdParties` folder named after the vendor (i.e., `3rdParties/Vendor`). These types follow the same patterns as requests and responses for all other API operations in the codebase. Except that these ones may use additional JSON attributes to match the real 3rd party APIs. (e.g., `JsonPropertyName` and `JsonConverter` attributes). +5. Make sure that you trace out (using the `IRecorder`) each and every request to your Stub API (follow other examples) so that you can visually track when the API is called (by your adapter) in local testing. You can see this output in the console output for the `TestingStubApiHost` project. + +For example, + +```csharp +[WebService("/vendor")] +public class StubVendorApi : StubApiBase +{ + public StubVendorApi(IRecorder recorder, IConfigurationSettings settings) : base(recorder, settings) + { + } + + public async Task> GetData( + VendorGetDataRequest request, CancellationToken cancellationToken) + { + await Task.CompletedTask; + Recorder.TraceInformation(null, "StubVendor: GetData"); + return () => + new Result(new VendorGetDataResponse("data")); + } +} +``` \ No newline at end of file diff --git a/docs/design-principles/0120-feature-flagging.md b/docs/design-principles/0120-feature-flagging.md new file mode 100644 index 00000000..088e5a76 --- /dev/null +++ b/docs/design-principles/0120-feature-flagging.md @@ -0,0 +1,59 @@ +# Feature Flagging + +## Design Principles + +* We want to be able to deploy code which includes features/code that we dont want visible/available/enabled for end users. +* We want to be able to progressively roll-out certain features to specific users, or segments of the market to manage any risk of deploying new features +* This optionality can be attributed to all end-users, or specific end-users, or even all users within a specific tenant +* We want those features to be configured externally to the running system, without changing what has been deployed +* We want to have those flags managed separately to our system, so that we don't have to build this kind of infrastructure ourselves + +## Implementation + +We have provided a service called `IFeatureFlags` that is available in any component of the architecture. + +> That service will be implemented by an adapter to a 3rd party external system such as FlagSmith, GitLab, LaunchDarkly etc. + +We have also provided an API to access this capability from the BEFFE, so flags can be shared in the Frontend JS app. + +The interface `IFeatureFlags` provides methods to query flags in the system, using pre-defined flags in the code, that should be represented in the 3rd party system. + +For example, + +```c# +public class MyClass +{ + private readonly IFeatureFlags _featureFlags; + + public MyClass(IFeatureFlags featureFlags) + { + _featureFlags = featureFlags; + } + + public void DoSomethingForAllUsers() + { + if (_featureFlags.IsEnabled(Flag.MyFeature)) + { + ...do somethign with this feature + } + } + + public void DoSomethingForTheCallerUser(ICallerContext caller) + { + if (_featureFlags.IsEnabled(Flag.MyFeature, caller)) + { + ...do somethign with this feature + } + } +} +``` + +Where `MyFeature` is defined as a flag in `FeatureFlags.resx` file in the `Common` project. + +### Defining flags + +In code, flags are defined in the `FeatureFlags.resx` file in the `Common` project. + +A source generator runs every build to translate those entries in the resource file to instances of the `Flags` class, to provide a typed collection of flags for use in code. + +> This provides an easy way for intellisense to offer you the possible flags in the codebase to avoid using flags that no longer exist. \ No newline at end of file diff --git a/docs/design-principles/README.md b/docs/design-principles/README.md index aa8adb01..64183db3 100644 --- a/docs/design-principles/README.md +++ b/docs/design-principles/README.md @@ -8,4 +8,7 @@ * [Dependency Injection](0060-dependency-injection.md) how you implement DI * [Persistence](0070-persistence.md) how you design your repository layer, and promote domain events * [Ports and Adapters](0080-ports-and-adapters.md) how we keep infrastructure components at arms length, and testable, and how we integrate with any 3rd party system -* [Backend for Frontend](0900-back-end-for-front-end.md) the web server that is tailored for a web UI, and brokers the backend \ No newline at end of file +* [Authentication and Authorization](0090-authentication-authorization.md) how we authenticate and authorize users +* [Email Delivery](0100-email-delivery.md) how we send emails and deliver them asynchronously and reliably +* [Backend for Frontend](0110-back-end-for-front-end.md) the BEFFE web server that is tailored for a web UI, and brokers secure access to the backend +* [Feature Flagging](0120-feature-flagging.md) how we enable and disable features at runtime \ No newline at end of file diff --git a/docs/images/Ports-And-Adapters.png b/docs/images/Ports-And-Adapters.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab7622d33e1298f9153388ab4352afaa55867af GIT binary patch literal 138409 zcmeFZHv>pWclW#TJg@t? zpXU#FKV2V)&FtB;_ljd3>sY@ZVT$sS7^uXkAP@*cT1xB_2!sgy3O|Yh5Bzvf@evOA z0q6Kh@&l;!7wI-|@XS<1P6Pz1h(^0JcsljDos@wsP1Nm7^R?o9KzSYb0{He8guwF8Z-HOG z`N1VU{SA0QO1cds`|l|~-{^y=|DGK9;?qkO(0|@b_ngr0`M>XtdxqZj?*#!bEL{em-Ky&0kZ%397-lxzz42@V3jOlB{_j)n7oBqucwf ze|H4k2>*57^gByN&|&2uQzg$7U4ypGP!j8mwqxXyt1u#(x{68;a}^SPvmz@YRpJ^c zufxx!f;tw^r3i!yMN9H<60zu|j#Q@lIY_&Ws*&K#gSmqjL$svnpfTP1;`>tAQ=}N} zk4Ykwi!k78#kwx`|Cr@u7Y~&7ew~Q?w%gxGKknN(YiZT5cld(0A3(aX$+@)pASNbN zK@h=F=m8>hDFO+D_ziN`agnP<9y95rw*mRnO;=KD+`_p{7i(0a82ZI=nhX)Hzn?=G zP=GSAl`=SlTfU;nXnsrZ!gq5xxz6r)2Z5fK{mt370}?4_$daC|$hCUZM}O%py|eI^ z7k(S)EFDBFAOeCSLO(Q5u}gp5L-Ea2S8{o?wVue`t0Tul33c_G4uQYVB3F5VV5ipY z8dtXtb$o>%QSv;4PYx`WzOexzp%E}~kjFqb612`ri*R^LI28$r*-aoR z3ERAz6Y~6WF23cR`~LKZ_;6J1dfAK3@2HGG2LVqve)9Dh6s;{tFw67bOu9k#<^v&Q zS94oVGe9R!GSq{uAWOga51Va12KMj^=z1NC&lx}5T@yb0g2SrigScM(wej57mFt5n zHjH(p+5Pk=({*H*IQsNE^=q}k#@*7UcbrRvd8E9mKa{Mxlg`W3x?&ZC!Z;Bio2wo# z{WcW0;9|(xN4M`twA$6p9X7zUR$Wo;ZCez5L|Z-+AP5orE7R)8a^m^9I|X!2TNMQ=q7P7RY|jwUajsgDqTJZuz5JrLh=586fDl^pu$iPh5# z$J^mgI!WEAmz6dYFsAU(5`)z^NPa@lyru{%yVhkOe!?czm=HGD~#KlK31;o)3pe%r*fP0d3A5w=(q z0FSgnUdmTY4;BSy@9HeH@32kdT^KL1;U2|4#yoP^@sPs0 zzQt?it2}R`_Y~V?R^#^MX-oJ99z>29wZA1^i}Q@JM`GWy(D{zPNT`t4CxY(bqeyaQ z6z0K(B_XVn8abwO@hVZ~oABpL%NBwN2_Ry>`j5^auzX~!}>Lu{{F^Z@$@FNUR-yi9)!zsrD;_ zoX=r{LpZj&Kb-S{E>}aj5{i-qNuAK|u9JKmIeNn6Y_0sGoV3-Tm~HemrXM}(#RR`2 zXQwYAt{lvGG2=%*<$q9C8L~buqLJ-&Ng5?9aSk$WQzRw9$4!av2oy$=VFg_Qws{Ka zQA1(=Qa-%8J`3jZECZLcmxSU(Lt#qzVh!a*sNdyTQAepHqd0`Je=by9Smm&w_Vy5W zYWKr)e7vrdk%YX*WMOB64W|oJAP(I5|0qz8cBX9td+Rz5bM~x)lLzSh?=sBhQD>3M zAI8XvumebN*sS@(?g}U5Vq1mvLt=z!^{Wh32w|s#8-jC0s4Wd>@*?m@1EqZWvIO*U zht*yzTqOzdFzNS)7d>~z+dzULqx|_;`b_p0;Q%h82s3`+3|u{dq?5vho!4~hbPq!! zvA#2r0Xe<4%QL918VrbL-LC<}#d|NMjV_~3^%-mr;mdzca4tuPQ$4W)xa|ozp4JRM zw!kIML%8`n%KB&BDB0TnCxJ`O*ACCaZ6q|h0{1c};J-82yA)r+6K0aK_)@E(^neRF zJDk*@i{K%M(gT8o)vm>z1Qr=uC}Gzu>U9jlDf7(p2N#GxVKnKpw*mlYXl1U|cGjsw zPZpvqKe-$_O*pZiN_^*O)n1BC&0I-tS|OBtG0lQsMFLr%o|iJG;OGhDI54V z4*PS{Z9OVtLEutZ7+fx`B#JCt{YoC@Bg9fWY~sss&2O@f5r)byqZf3b;r;s_z-b_!(RG&*TTe zfgw@58ZL(>6B$=UuA1aj1VjtoLHwcy+}zU*d#yAOtq0Xd8*F5JDRfpE0>#$Kn}CkZO0#G}m0!!FT_2Bw`2F@?`(m6KW`Y27ENwH*;6eGb zP7{kJ`US7oV=F^SxdbKXJ=<3Mn4nr}8Oex=Ff<3nOT(}?mEc}?mV}w=KNzDr7hhYo zdlcyf>WcS7B%i?JL&XIG%O|FfFR2x!;+Nr--7R%*JHPZ`d?8~UctIv(ONPdG?KBb1 z&{MLYyIT{Ufnu|0MBknycPV=TPo}D#Ts~o!_5}Bkf&R2Tbi~C)bp!_o!OUb0wu&7f#EX8lCGlMlX`F}X z6isMPVx%6t>G{Rwyz?1|EvH8A(%cFEiX<$TvG?|pd^)tRE@}P9&*TL&TqVy?69);c z^N7gx@eI!WRrLPm#FBj=be7$-LhL2)=U(?M3w~n4Ugrmg5egH5&)8KbOGPvIrVyn&s8AvpS_Ea&lz2P7G77IEFIjM-bkl~xZOhJTcL;7>g- zcE}*A4|aa<$1B+8+=lAcBAa^9ooYU+5&E(6#(9j9czh*1XH4>i=A~I9>fL%na>c)r z@nf3FqVyx5j>bg2T}vB6!WX$fP_=j5EK9%}KQ8SnWx;&LkkOK_#BV-o`Kkx5|ReINf68Vj3+HY^RVsC+t_PYOp)d#9f-oxnMw*>eZ za=mR4??TM`Cdn7KORnfGg%vY&2C%{!9Pfl?{F4#RT0cO(AO8NOm_H2t!4mb#iVUhu z;8fELq`@^I_lE#^SKfg5c_0HSiipeQFMFn(G`FZhWs@J^WIwp{`g!ExjqOr-1PvM& zm&%V+q>k@8PYwGI9*{}SMhI3B$h?UWbxuJByUQ(%wIvz;d zpwLTsf;#y28{S30QNX9Nf&;XS?4{oD_t&zRY-ZOXT@XHlY##d9_r1wqnmTAT4IY$u zc0=#*lismMO1sv4VY(D! z^x8~oLJ?^rvSl+W+-OWquA~MJSc&vJYri=k1Vx5-ghYtGNQiRXl;{g$M`21^m9O2Iw(ise0~ zxO-stqngs3b;L;pCZP75srV2TBjWvmzX9udmf^aSpu(G_ zRQcvhHR07&JKA9Xr9<-+mabx!ob;oOVy{2=40j`bBPd%?}3 ze1)N#z2RH?()N_6g4?~%23WWBFlS>;WFUx#>Mgyp1EXk=;;D*27~PiK#qu`e22v%( z1Uzx4%U_?V+s$Fi{B#}2o>&Dd)cRMjo%_~N@wF&XV$oTYD?f;#JxOyP2@K$i6n{w8e13JqAy0$@s*tm)?ZE;2* zhHM@2M~KFcr8#~$vM{6d1SQlP78_b3pbxSg?!=$zSgf60v5AV?>O;aSiV1u zo2i)S(S+bd8D=dNAZxXU;*)u974maM0<45%oQ^&v65i%uN^&GRqx1=8PLRQ+#UZiyUDG8Q@f#TxM~3yPjumdCcM_&nEL zRHv$yO3z;G3&WAU?w8W*ie#H`{E$1;rpP68cb0cgjs$S`{N}GCr7~$-Kq-1(#dXJ< z{usQG#Z?4QoHkL)NSI7UpvU@q>aP+dqeE%R54^G$kZ(o66%-2-1pVA~S|{F8Wb>xr zEiqf;;+#h4rJ5ihcv<3Bo>GOdrl&I1RCu56DnLqD@*@(Bh_M7+Nb>;ePv{3nTAa#X zBKv6!+e%ph6&UV+hb#@NjZZ>skK6jg#IeMd?%o?1#fr{;WYKoD{*ZxB{%-eQ%sF%# z;jRe(TW4m^W{t?O-1SMrB<>FW^)W!d8lPZ?jQ78#QM)(4tYIDAgk+M+r4UGqps^n&Gbw5JRduy%1_?VnNvZAc=DXa z{$wSeh#Un%+pmHq!xQULqMT12Ej73ipj;1hP01Je@*3SwV2cQehh*6B^%LrCsO4SS z=zOukU%axI!m=o@lzFplLJrvsuQ2nV!GJ6hqOOmKDop=}qa*&LMPOmE5f-h$GA70i z`Z9){^v=}d6BC?77&6oVoiI=7D_eMN5@mRvLwHj4v7=DR4Aw+#$vp(hd(2`XvYn#l4$=RdlQ2bGO~sIBp%SSyMj4Q0!0Jv z3h}`7QY;SO1)}(HP?09Zt7sc8j6MqOUjl8&;0M+sb%{Ag1()EbJP-r&;2_Zz=vhAa zFFGI3;1WWP_3i#?Su$KbsaSG@=IHy(Gm8{wB7)cwP;jRBo!bj3#sud&S@FQJ2C9*% zr2Msep$4a>H8Z<~$%xD=A{eSJVQ*kV0mq_*ZAr&?9)0Qb{U1x-_75=q?Ql`0Dv;K9(lZm;LoEb$b6loVk!>91cD-u_@bJ1V zBIKhF*O>i+bmKAyEyEwRmhWx967ylU%P>*#{M0O_h!9;T(m)W5z}D;Y zEc&Kcsm3{y$cB9$tc>!!pwbUI%=iO5OKSC-#W&c{j*V~n6C`M6@Yw^)2O0l)ZbprQ zx8IzEv2;>%S>jpuFF$WywszFK+3wEzOE?sZYd{c(cj2y}9B$~uKtLrLDlK8y-EUHg zuXz0URUp*j`AG*r=9vI23@93OzG28)6bL(dW_@&7TU0X6w9m3POP6NG1!)mp8yeUV zy?fzmoEpz6&VbSqUuE5>%tV3v(@0n_1w(d*u*7^D^a0)k;jJ6PE00d$vDS(UzF|!V zWkcso(lD$f3SLcc4@A!D9hqTfu6$}Z`6#00%Y|Q`ow&yCbK$l`4Ke}i; z_pBD%nbNNOVd4=K_V=i47SwP~`bvtWigJMpwIuIhaK7u$-Q<@Rn!RTknI(a{iSf)n z?tI1-xwL$OdsMB@fZbEzIsneWM~(eTW-JWTM_>BOU*fTHzxfibvSXe`Ez|)PyhUW5 z6iHRUO6<$Hb#JTNqV2~HRE$=pI1}))C0W^bVB&|wKTtbqtOfzvcqho z#Ol^_FECDr-exr14wCigNV@nF-;et$ZvdjU#6;2`cPwrALp*n8Bh;Y% z>+Hecf8S{zi3XhBxrhhKD$^y=-xog7{#T@a{a*zih}Wm_Mm>l}=k-UroVWQi1Vx3V z*4EB92=;X}>&L-#IdKVb9^|jO5fJR)SmHxyf#Xp0wzyB4t2^#~xLD|Ih;5(Avw&kt zHjwg1^RS{#xK9FrLJLxM5B&rj_km=A>z1Y=OC7g?v_Y~Pe?Ny3fd#lw{eLf<^Y*`{ z_-|DF|CJyz+EYY(cUVk5&Qt=qjIT&4mb(@jg13MBzjHdL0NsR%yL1}6 z4%zqd1zF~pLJJK2ZG5N8j~mDsrNIEJ(^Ot&0XMIY*Vo4`y+jf+ajeS6QZcM+1mZuB z?2lD@%ae|m18Mxfq}3+GFYmnE_`tYYMld_N15K}N^_=a1Hce2&;cE`dw$Pq4t_^w+M{?8fCW2?WxnB#d;xs! z#L873Y1LTOU6C_~C8V9>p?sZ$UgIWXb!D-aNqQ6$PC4R0;bFt<=m zX7tU!pO~{a&7d&KyUY9;PHc5p;>gP2(OE8`FAAE-ecPfk32Iys1;-j%+xL_6sW=BS zsH%Zu5qwh*zQTUvI4%ew@j4{K0*?E}DA7qeiLT(?gP{q_JBV0RVP#=MOM5Wyh#twO1~Uhp{xUwDij(wdd~TB# za9p=AYdRUXVao~qkmv|O$OV|l&s@#W*Q};~_|ve0N$%>;D%{eR7Re;ZRt_((1x2+>Se);s9=^Bo zT<;DC1$B5CqHl?a9dD=+OBC_ltC!jkXnPeg!n3pNYYSq1UHt-7lcWLBY7X&?4;P}e zpFRr<$&eN5Ij;stL5w#r`fR15CEPmo%v`T(GS5V0vyi(=Dl3uJOYh)V$;+@1Gs=g5 zcM=}Wtwm;lWr`M(-iq?#c4U$ctVL3`W|9VsFe!AE>QfsV;wpE2ei5DggKTS#P_#+p z=SNx0iSF_(I0QJ}DS>w%2~ z_xj{N7=Kls+`Omdb+0Qx9uaW*^V;yUa^6AWaAba=L{@G89i8d>U*AS3I`6=x+M2FS z2!sB()|%-K@qE~M72+>kBWqAuiz&Gj6O%=4w9ov6__c(q*MX=e>rMmN*Tz)HE&c>= z^<2x{O{!~|78G~T^)b>*Z76Dvz(w=4OC*pK>g=ztm6V#7+M1m|u;S)!TzXJ_(I;JV z%@=~;q>r&vzmZHX+w&J;d6&XfBUHnBMaksa$005{6pckdcHEhN<&Jy5mOgrLWF|8v z-6AfW6rxDGeWHH^YINE|&Z06QTr0@8Xxj|Kp&3bW-zwG!Y4^TNZRyTu#fU61bocf} z_h(%z6WCfe{r;O(_<9#3-55)5_#%7&NzC#rtee9GAwa)}=Bx2RX5d={bIaYhOj$FEp4at8qMrA|HNoTkA-@9uezB64jk1ch zxtUXj?5mIu#W1G{Mhf3MI&?Gipw^p^#NS1(i}o{%4ZW`lrzUIkUzD_2&-sL;>Ur;D zoN;*!U4*7yE_puQUEAt-?qf*RGM11NVdhA=&hPP3jt#*s7jOS)+3I=B;gMK>u2`^{ zSV|IpoHI>dHjF4CA>Qx%5-AsjBhRyOKC=lPSH7HYt!&i5j4Em&#p_Yln&Gn_CogNh znO9a2PL{=xEGPJ)af|BC_Chwz^+EXYvi0%|oSix%tY-&1=eBOSPRcYs|5!&v7~6>w zV_VVOn{GEyR0b`L&Pt6Iept_nVD1gv+0ooE)YMwUd~3HRSJ!-g!Jn7rLT%E&-4U+B z1#>FuCHFnaX~sjV;MMiIj&C|_IlS}LuI`M&spwPTG%Oh?0B`>p;ysyC)9ZaTOL?6v zNv^cBY#Gna5R#KahrqH=OQ|YTM4w9TatXU^zS*&44zCLH!E7g5j|Yr@ z^d*F;@v0Jbku-1NIy0#WfvX807>!rcJ)gHMYdz|a@lA1`@17-hUmx>L@%?@DJ6UCl z<8`ZnW?wo=#pUv2o0cuqd0nYDL2YtP_TfV7P~ehO{t*0wF|;Aq>D`GtIQIVTlo>Ge za0s~%$BS^=itepM-7Z}FrB35P0iEH*WjF9c7){#Z_)gckO|rGC+r^ye*hb^7P1mPc zylC@x>i9!bC3W|^FxN+Z@E@zd5qYc^PF;S}_%(Be>CRif6@*WVeM)UNcgtEEj(fC% zZJRZ$VHEVJ;HIl@gG(N}`<7);a-0uc-GqNgxzx05)gg%sYCNi!MI!>Ii;Q)PE_?Pg z)ff~?Dlns}d+eDC2bVc#tg`^?$#ehKlb+pEEa5xaGYB4&Wis8c+Omf9e>ke9#lTRo zV&g~)^46^wf!FCUccPw8{4zyZfpxnd+xBZ;CY{F5huN0oo_k@6Z*-RRPmy1W22bzT z-g3-&&-5!lo+L&B?U;v`Zl%+L!W2VOPf!K=j_}45yRG5oJ4qr%GMam%;F*%jQYP>xhTJpkVVr|`OFnXTr&)q z<2CZ*!|fr3&-$2_?foSJmOyAu0-5mrSrwh~MfaXF9`P7itXVCO3a8tl;N@T@OQ9sW z`(DC=jqmzcZz99Bh>O>1SYA0XP@S-aM}-ZA2jaGTIwQHk3DtNDjotayK35`#t;#-k z$GyNB3eT&f;SC0Z{K}Ia_7NVn$40FOp~J4qa3t#u7Eod}dRuk2l+!T2WUciYme7@= zu(4B*ZR`ET7C71#dX728In0JkIncQHr8nK}17J0wt0U1j+~`|qU0Z?4H|RMK2az5huQ$MV05>m#sVQWic$2b*VHRvR5u41QB-qZxj0~7;oL@ zCPoAt7p&`qkJ?1h2pGSS316)QU{guHMi|`FRML37T2sj4Uu8<@&b%T{wlYnzuia~) z7rNWZR#xC8gPS!}sd)Ly|E%R!Zl?7h0l4kghOL9c;1rjvmc;*4{U+n)3JQBFd&xZ? zZvb!IoebLdrt&ycvGVV+*x!;-+d%iD_!Z{w{n40i4m}SC$^P`yq@xCZ!-&MhP2DBg zOOBd#h;fN*J*zB|fRI+*WAfMW+Ye2zvHZ$Q^U%b+DqA=lwTPwyVoo8Y8kxF!J%qv~ zt^PLsp?U0re=krh+(({wf9SRGdG2SEs@uXM#zY+#M31Yx(~*Go_2p@@%H^|wPz!(tU2z;XE!V2|b2^E-BH_|%pE>s%HkVrOZ+5x$d}BXaL*OcKYn&Os z?@?3uT)_ov)>_RKiV)^hW5+2{OxM$3k?wy`H%iKr@( zney%G7VqYRXmZdpbJN{`kwty?YH_he7s|rCgs%6LGcEUOQBi3NK6`OKoMjQ(+Y-|> zANe!iITf}caCa18#Rm6s=qk)O5jaRFa@w1He&snpmj@V@-Vna^@d3yM+m`O>sTxM9 z6M$vVq}aXVW74iSdqgm?VYOJW=a(N?pd1fd>plGO5zDqBtZA6YPevO_e_rWLkx9fi z*&68iv=&eL-L77pEw9G{S+7FinoAe2QQle<|L-}|ZaOrti@8l4n*Cs@uuLn==)#+W z)}bC8yPk zrm0ok_*#cNKHpMGUsFRTe#LT=v3_*E0aCYi*`Om!eDxWc(EVOjOtN)B&V+E9>~z{M z48g~ah^^nVYf)u|+iyUFOoR*V>?Zw6_qWTBL=|(YE+Ki1x&8B5MwOiaQfh0f9!=c? z5b}2FU4Q3B{CQ~a*46Bq!pe9?;X`oGG*CB*bnN243*E1jNJMwPG{~`Lcj+ydxM;d) z+-<%4q#e+P!M$iy)xh$6-SOE9o$}>_9C+!N2C)3@F^&r>BTtt<7D)?{A(u)0+9$<# zu8g0={*?$4en^0p97nwEu*I3kxNTO`AK5?^FQm%=2CV`U_#8OiRx`8YM_xXn?Fowv zjgmLbZy6IZTvuK8xn<=0mFhY4cyBI>Zm6zeO~#WyKfmg=9wRx{sUUnGxHx5TYG@Tp zy&v|RQK!-VAj5EHfQI!{VH7>k7V@H<7$nRt|xk+W<}R-TDm_@6MmB z^B17zXJL~lZ3H?_bbJp0Bxt&?)ZOP%2$-2MCdl^)5c%cFw;Mj(40A94oFJLup8d=@ zJGocCxpd320u&XNdp{xs=H3QKAT4K|% z!F6`jLvo7HPDssRkSFbs;<*o924KoemQ{iNp|+ZjwQ$~J7n5K1O7(X80w@n%%btf# z3KI6b&+o)R7dIUM^?cc~PYr5XY|o_*X+9+qxt8ZP=HyBF#>K*8fVat9r7^g63GR1ce-;31Q(5AaptV&UjUY<@rB=de z9G6DXfZ)kj9ht}w_q#xy>gh~kYIDvunM8(E76SrV;sUFsz#o8>Or4vK**#EjvsK=gD=bnS;*_|m?lOY|K772eUTNEtsR8I zXTNHrS^5az#xDwvdL-#j@;dJ*+?Mn^!`XlFAm85sl>*0QzNWHJV~IbN`48L%M2?I6 zd@e6Ndj$OGL)r7^w3pSKt^)FVe0hEb&vBRXM)os7yk=&p|{dX9{|PSTcv_~!1n zKCSAEK3U6g-(d@mT4V%M3+|?Dgv#+X*aIYVN}7_uTDV`Ae01-+oINN_ldh^e=2uR0 zsQ~FUROyVFl%((uv@L5su@E!+IdF_az52tSi5>I1ju!m^hlJODJ}JVdx9~@+hpl>z zPTK$`(;o`+?Ugs+t+u~_MpcKvsdIE~VE6(HmbdmR2g{x+TNrU{o7xMCot@&&;K;Th z1i=)?U+$HK&B>sj%it*RM< z%-qW~oOhVHKTW<@aX#Hp(~d0D_4O9|QTH^iP13zi=p@{J2PHmO!(Jz8?ZZ>~$oWalJiI?i0!IlyTq)NQL%plSYt(gg zkoA>B>^q+4W!!}vlblkS0tAaV%mm<@aqx#thd$_RKZ_sMV~s%%77>iKHdYQ-2+wgd zWe21b=>B%(8kDU_S6MPlB_+LKN^kf?&jRW#>za(Ed`|lF1eX-SRm0mSgP^;k&gj~C zZHcx1PwVOt;{8U!-nS(@4XHji8k{xw*XZTwZKf&i@}kN`!w1150*r+!>D~v|7j;*m zpTmS=$S)TT--``A`EfHWHpT=ytarb@3cW3alnptP_?8{sNOEJmAJh!cH9JJ;j4CmjdO>?xAdMumQ$KiWA>s+=m9V7)t+t1)E8+A3M|_66ovZ*M+ZJ1uu(3Er4#; zNV)2hvZc6PJlxgcsXQrLbx-Ei9%dQt+kk;W>YtUCrFhob-T=A~V!`i!&2$E;xpa|S zcOWqd8PEx^czEvg=V@vQo_I?!xuR^8>+{h0>IvUN9Ahe2X#$XD_LiMNEt_e#GkU%& zud6m!PAiQEFP3ioUW;0=axuhU`?WsYBwLv%2cFu!VMNodTk_yHED=0JY>?R1c;QHS zVBr|fEzT>>@CY6zG`yQutsDOBy)AxxHs2A1b~(4{H-Dqrr8eZ^olRzmB@=$Ef1VIO zGsu-M4mvo&H&a5edZr)i(j9W<1610qbf9jdAhuv|y}ZO? zzq+`k1up3G>8zRg0Dw^8&2bcd%q{EVeEWz3xq{Cgi%V-#+;hzupR0Y5leE&%S=e*h zfxF5KA-AdJc|oO;NAX&5VtIULUcdA8?V;~0CXV{Wz1oGi!X4_S&Va8blb{HTq4(9r z&8kZym1bjEKLoD`D7B`Ie7+|K4L1nst>alLLFYbLo)_VxC&svKs@O<20-c?iK zR#p9uTZa9zjQB(nh<<}(!yyFxiH(a#HP5a;rA>z-!t?wWVgb7J=xtm6*jya*2S5XU zb^*Iwrf^++K8>%E>>=t{MK&XHwgRYviYh9R>aQL0!!Jzzno!yXVdsq9R$$hNIRjvS z1CdA5T_rsr4(<|bZZM9jf8BRYI!bJ{QrDRXlupLiwtV==Q4h1nrVW}6lCp3AXW*l0 zalrx-ZR*t1opYPfxzw+7KcAY3QNnKHBH@)lS^-CWR|`O^?4Fw}=Hf1rld}*F@SZ&y zezboGB4qU7IlfXZv@F*3EfxjgPZZ``rSK|87i-xxkU6}{4L!JWse^UhkH{o4!Cac2 zC&A&)A!C#tP=f$^fRMyWi*pVr7(sCnCpbu5pCO>dX)eQijDcRroQUt_Mc*0{dhyB4 z8B%_XTg9t*75rk<&^TSyvCtJkeML@c)8bgKjUQd;hd>X+)@0ZY2vO&;3cTJYHp-Gw zq}DZ!7F}zO_}5>naML}p{a6_j=>xBMclS<5qotaU7aMo6csD*Dm(RH_d%NB}r#Y;d zc|acFIm-wzAt84gE!L=tA{6#fvy4CkvsmkC`m=#1qMpq31{#3L_w3%Uq;4)Vt5E6L z3bGieM=zoVZ%t&&+T6Y)nA(#zCCR;SZ$t>EZd2f-58PMU_q}8lDl~|!?~D808jv`B z&!^P3+<^8dQEd`jI!e~hlGeA)Xp-uV*@*0a+>@*NOkV@RYeQv5cENI)h#gDN?N8MdX*Ch1Y zv}Wf{`#W>0ptY^|`|3gvQ`}MTy=p5~kjVr*f2A}u9kR+f-xym9hR@)(5wEs6;(Hp&y_8aRePd~RmjRmsvxD?;JC8-)xyF;6LAc~;Jj+&6@yN$Tc9^nm^EjU@Xv1?5kl5~K?MS?Qze0=(nwJzC`7 zu`2(3EQmpPW|Hf$?os5V3sCuR-n}EKCe|NAJzMRL&;!bLDaz?)>s#;rnwgEJs00Qb zkBQ;BpW~g$Ke}T}8B2cMnfhVxRQGbg_XS1^KJf4fUCgePXj)zi={<`bRd|p|v=XvF z@5saVvqovV-dvD(KfJ%pu8=JnHh!kB9V`(|uB8D}U!HZ?ZMBgUeyVv~P4?cR^e?k# z2zI1_s2w`pVg&Bt=8K4RMa-M;e1G3u6RUtr;J`k0OXwHGq$--w2>{>5= zn6aEh@gD&D0ZXc0fd;phFw{|_xw~A-&D3`IQUA{ZLw#OtXn(i<7 zS+)V)jRt@*CpwHH7mKxRm|2E%yl_(&VxTt&`FX5deKNSdOX%kUZ;-N_4({Og`b|s zb^OAkcQ0_3=Zr(R@^W{gHa-gQ9G8-yhw~XgwhsuhuH`9jW=mgo7_?48b~;1M0+w>B zyL3SeE`F2@)RWPqd0)5#%G@TuZEFA=hOYa@6!c=gwHDKnf&BS_GpKeBMu_Lp`Nm@R z$0@>*n?35c+j9j^ahS z-borws_A(TTR5wzXh?)>Wpp!k&FJ{91wN0>m4&$u#Cdlw7u+7OxdWJ&Mn~_w?0XpC zrSNA^RW+>AcnVj|@SCkTFI5{0Tj7rUIU3Tw#e@-CQ9z!LmrlKXdWTpVm z0&i3Vv#e-jz(22$On=|Yy_9r_)ia~jY6WOBKGs-mzV{0msWb;IPTSFd=(Nys3kU*s zB}xd|T9&DfvoXxAC-bKxt)ZaD#ZzX1gr09@;V?O~aAIxcpw6%g{gI13J{|{`htf7k&>!%4iiH8aa?buvCz2Ces4 zg#|TN2q+D3PAu(psypQor*d=T??5T&19!d!Ft;MaQDa_c*FF#G`EI2Pa43$LK#IJN2J z)|0yT3#4hPO=vT0XD++eb(VGScgsXNYCHkT%}uNr+|9vQ-@Q{lr&Fk0D{;|_LdV$! zwC8C5Y0s-d;0W!}lu9b3-FFI1%NGPrw!m`3@5XnLQ^6lPPn`uCQuVgy!+A7x0 z=uXs-?O!%Xk}KozA*=h*krHbC$K*V=>(ro7tHZktQtsuRw(BtQVcn9-(xC%W{wA}VW{4HsXDUCi8a z<*+7Ko1bqLcZmFppeSb|k#ORlsOg2{fdH5z*m9I?%QfeOCdO6+#V+G=mnwc9O#RR?zYyr&I{fhQMrVeZjH|0 z=SA?B#)MZj$o;71%4u%NM{ldoT`5Aq`k|z%yQ}DhDbCTAR95WWJ?@O&_V6BE5STwN zdS5~bpPL}X)ECSh15?Gdbl40?s~8m@V@<5FM{�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

rQY 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

ON0bcOfn&6})M~ zrt_ei&-}OU;O2R{v7Q(WB`w66(wU+L0xal4LHhqrNUINqY`rmg^c3`@#x(e2H$cG2 zkcdCCEgdv3U~d&%6zR+0l9cK&4C#ep8YorNL9@Z>mj=BPf>#~WM-LU!`P6~zVrktW7KCOW4KLGpW`TOh)c zHY1t}ZjHV7BUD8Z)VmhtS`Y=PM}oc*R5?dqI*ia4MV~>AWksMxs^BRg25nb`ctq3C zMFM?~p-%*kl%geSxCLeI3I&DSrih_bHC&A{)I~u~eL!^qh4l=eA8NQVk}-ic7Woea znV8^8$QY+GVF3K^@8VAiic^QNi!F?r)gj~=NyNPvuumJ=5@=?GQVI01M_mMRIU!*U zh*`mxiW1dR2>@B3DVFPYaWNrj<+X_0WvI1(GF61oT2m;8xMn@G!pvm7v zN0WqOStlLk`-0LcWAJk7B^~WD2DZ~DI;tZaDW`eSq%MybVwgZ6uydK;WXFqnk-0hy zAh4I2&v&TdMQjt?oHB5a7u_Zt!#%u+%M={(1H7mql!t}3m_mFnKk=d?1UqS#7j+Vj zA|4zK;bz#j6#Q5NBN7KWpK!Vw;gqlISRaChcIUW8-6FCRKbIH-I1 zkQpDZ7@9MKFlJKtkeWHL>$3S!FyZK{;zN~$L;E2g`a(Dmi67}(faA0wKiXpfofGzB zk`xg;ek864RK5jJEpGg1?EnNzRfeK?Nw9q5_|Y#5d^yG9AU`r+ftyeq6ZjE(1un1t zd+P?%Z56Cj1pnK|eJdE?qSZA_%`Qb^fyd~YIn z-Up53Q|ct0lSP?g9~G{W0`CZ|4(6sUKU|o(IUcuwvT2baGt%C8c>6PxQI=B+({)9z zV#0yDneQzBmZseo_E_bp{h>r zaxl-!JSQ4IQJrC1s?lo6{?+)vP~3SW@}~RoH<>zjY0Byno69$?nSv0FDb|Tx)oXa0 zq(Xz0Rs=j$r>{vLy$05gm2o>>-b1`Z^+i3w&g05d_iONX{@^us!S0(~C~Y2*Po%OV zP3D@qYv6}nDDV|?ms?cwwoHBlkfG@0oct9M*IyOM>t|JPYQqp^L}it+uWe%=HObd& z*TAzPSH1RwIC!nkgEpY&Ion(KpAT>svrkvjmvn369H<$>`>+GUy{jmWnQs;G)zraz zsufo?iYB_rwCX7|+XKW!rPn+md(M7}CW~31Npu%9@%#o&iGGR!E9}C#rdnr#pC+q^ z;{ye@@m`T%_F6>Xux(J*9gWc`PtV`w^Z(GfP5pUWvp0^cO|iPd(oCo~JdZ>^7v*$+ z{`SIGyzJsT^}-irs>H;*ckk61Es);lCph_VhO%71IiH{L|U#q<6% zRG@{&OA+AYb2WCqC0%#vH?9hQ&6=~zpU!Jk^9YWdpf*Shctyc*gZiLheNg1aJ}9Ig z1v76Sb=wOW2}!DP0tdp43?H)(;>PhmNw~yixq23Sr&8FinBbMdYqyMIz6POSWEpC? zI3la3%+s_v!oMb$8ZDma59r%(EzPGU*Rx7`!V|6oeLUq^_&%}Sj^^YId#!Sr{;aOV z@TKvBwWs$dj{sf4&Y@&Y8-{F|g~X)s`m2nk@?hD;0DFmRHa$CvJ^1Y;aa+>v&mXW0W`i#M4!I}Vo|x6F+z_8)O*TrU^=oylmr?F_coh+e4szg#f6rp(0k z;p^v;3V%otBCm?`_yMF|f(gYfECJ( zT|z)himFI-4S59i867Fi(+V#1!GD+(P+GsxqKaK#=-h$x!2LZ6(m(S85T%Wb2~Z}&@PRZu$T4O) zcLT!@aC)!YC|c=8KA{{06Lm9d{Ud#M|pJ~7ElJJ+Z69G`0+bIsdWi-}b{sKr}L8H=D(G{vkW4K^_iRrhAL5OJZ3JCcv+Q-W&}p)EG7GbMl7s!|?5 z6$<7m-B2#hRbLNW$P~Megx4>VK4`A0^~LOIl;F=*qA`6vofo48f0QQr!Q=&mcJR$T zeYn1+B36zWm+@lYc4tj&@cl4taNBP)5>M;a!I;~5Y+vI6%Y8hSL3dJ@9kJsccS^>~ zXAZAD@wUVhM5N%+!b+D)C#nf3>e0LcycWKuaSy{kp&NHyT>NVCn zyOE|x{Lxf$e$#WxJU{&lip;b5#1ydtKAeC(Hez~fJ>#FjMtrn?!nxTLD2W1T=i@s| zVOFJf6D^$bLgZN1BVH3v7VPI;NFnsO+I^l3`ZIzl9{C^MlU|qQ)a{kLkmyjir{<1) z6m!(kUTZB2uiq>U-Lq6B=}fYfEUfK)8dwDl)O$$uTJjMmKMe`)&S<{WhT)fA5(R`b z*SxBNqAMXPcqW_qIj%mp*tDB6EL6xB8iL{hXe^;FcF<|{J){saan5fPY3jz%f)ZDM zdYI~_w^@`Zn9xTi{(fA$U;?B^pt0cO)yQX;@>)NC{qw!fM9%WEMXtq>XWSy(ul!i+ z<=oFlQ(yFyeBM1!H03{kH1ua!5B05QSkXK93^p$6KH9B|(IzzT+(CwzysEEu(1WS0 zqHU@!>cO|hh`skbC z8&5--VVj^aEJJ?;;|p^+{Q1Y&0` z2kLnqXvV_>>zDd_I%qvPs)XKE0$imhjY}=Wo<_%dUrVhBSSc!V?wwxaKfG?CyU$UYn6dsHGwT+cG|qH z844c{BAYDfyEF-}ePmDsv?>r^dIM1Hqb z;|?Sr#Xo%-SR2Sr&|~syEk%t=_Fao$xK<&tEMWBbpo-2b&q@!drG?*A2(sO{^MzfI z4?c+1QC|LGnRX*XcmpM2AIATn69|zc3;&AGW!3GY_@Olwdv_rkE#$dWwm@}SWRHxI zA5~^0`dD3WLh#jtgi0?e_fN%-Q5~j@{DYtCEtBu!D`L%?KlQ{n<_i~m{7DU6QvY`&!k% z-MRW^3?E=J$;0kK6U1G2&+v9zHR}l4TCd^-@v3*sawv$D=A_2XrH;Y~AL)>QF8ZJrnuUcLav0W6EvVGS-UP_k>3|l=QAPTl34Mra3QT{^MD=S0M z|F(wZRYnc}%Z(pLs9{`B9WTqbf5DW~%oY9!hd(9e0+8t+6tawToy%mcGb%{6$>-lB zyqS)7|FZlNFQv-p4t}?H0Kg5uyYdA6k=L6Yv%OSK=3r1vz_CC!3EvzcFJuqI>!oK5 zexvZBN7CC8V^IA>{D;NLgWoEWxW0CNMi6+zM8I>pi9qSxPDOd%5nj*DTs@9k*^RK8 zxX!ch`^Jip^nk(XU^Kf?){BQSW{vkYJNMu#>3`o@33`KA(95r178`GrA)Qcoa5PUy zMbYiJT5QkMmGi(aj>^YdTW$jjT+>b_!U0EbAQCn`7)4ld;o&hDxW01{fw3~T*VNCC z(pH?|obhN4<4yJx>C=r($5w@;vil$CCkoseQjF{{kK0tcdCte=9ns^$Wd^Ub{ke(A z7ov|;AfHo+JSMn2^vtU*g`Ta#UBbOJ3Z2@K!lZx6d(cRH!-H(zFv_a&2L$y7t9h?~ z(@N7)(3Nx#BTeDTzQ5ZW*@0)}wCsMkHkv+ciAFp5)v@!X>mp9YBhq6RlQ)8zvX5^= z_{XwB7yVXd+K-!+fb=lW;!4rMqDtpK=uf(Xe3K)*jC$uU^cJJ%d)T0TzE=L zN@g25y)2Q@b%s|?;l6radMxG@9#(Q}RqAf&TsIV%!pvzkmY-?nbu$hx!N42 z+FBUr`v**4xCvj7KbnuY54#ugO_dry-t>X*?q)SiI&#&~AEFIj2uHrfm)t~FR;9ZB z68($Mq{Bts%1g!ywzAyV<#T;|#bqRV{}VbRgEwUv$a>(#XU&Wu92P7rj$DM97q>$% z7LX-o!dGb0H6tWxRor*p;BI{Cw`i3flV_7I3774jWyx03ExU`7s#@o-d_Kwh)_jM) zg5EE8J*?(%rRZou_CvvB)x9cL{fm^Ky>aP2QQzl+cVvh-7Rt*^BmoHNLU`cOOMLjg zTwmb$)0VP?GRu)3CB}-l=eZ7%(p4e5TJPNtq1c;^ixWeDtGvQNd!Z8+S2Y*C zOkB@XmvNlZIR|&QLc0eiBMqqsvux^f+6j#^3;Gim??!O0TV}BriR-K>`R1<5=TjcK_>qK{M+b7mfCwIy>%%Pft#(K&Eshf<;7>x$xR zhkf<&z3%60nk0MH7)8 z4bNd=LLRO@DxM!PwQYUw46olBcOEAe>m*4SXTKt7bW8o&;P84~b7eMP{!s;T1?dB; zBo;9_lN2bfLYxKj!(qr%N%!|0T?B$A`0;cKhv_^>{+`xJWc|A*yT7ERS2G*0!whY7 z>0X=({%{vEqf>8K_L@$P<)PSt(Q5ACZ>gc0lGXPa+Mm(rK9jf9Sl!3>+qTPEsN&sR8Z2+{Edxj_23?T@w>((s1E}d@7}X?4 z88PK=ZzbdE4s=IemT17G6ykrlhxLi&(dZm>W97$3NzlgBPb+LU&e(bWauK2NEq=qi& zDn;~ozP-23=&h+><&H%!W}Iax&u9KVmQ#K7?jn=Krj9+-&1)j~Cx7Wfu>j^eT_sil zsto4RUA87O7Kirv4}bVWxr=*4wY_=HYV7iOM|@V@94XSa$6PH~{iW1q+1aX|XBgOT zDs2XQ7H8Ep`p{%r*F5Ir^v0y87vEYD*BS+waV7rfjjoAN$4hnJ&1JzwlG_hq8NBWKY#%F-b#WWqBSzYLs4zbB;NAL*4fL8+ zZNuO;I{zC-;sKWT+pa1bZRNMJ+*^!^d}mkcC(YrK zIFbDMlqMx$$ApEP;bo9Ct})XI_MT`R3H)X2NX4Xl1*d8a#eM;2SZNsR&mII#_y&2nnw=&j>UD-atNA`^c zo5U{CRCgoltD+GfrAW=?H?Cq<5kScpLW^7r*cn6U;0(}yLs@U;hjMWRbN9?Xf;lQ;uFz~RQ7HTtZ9M)PTe%GqHP!vxlGBfo+{Yy3;EHhl26tBy#rW^k>|@+>G0#IqNGUHKy*kI2b$K* ze1&Wc98;5F>a8a$lfYYta|*?a&7xm;PhwDZLod5n&qUC@-pL^OhF|!~REANbGqkCk z;c}D~1#O!923fHBRQ0Jtp7o(_ox>-Vy2tNmzPeNFL)G&A^b>YF9%-s^ zK2H0jsYgNxMh<7L40y!^PU7;_c&4-j;*r5#Lr#DCo7w^sj0q-(3v*-aId{=>=3H=dx6?Ib-G*O?h`NYU zodY^#a(YVhVBeOOgX@dAONsNVWM1(s>R&bSJLOnN z=~(Zslqi{cc(!{ctjI5wEw4AMe|uiWe<(+OtqlDWxbf1NY!xQ1IvToQ#9XKD4B$ra4)$rB|>{}q@8%E zkjjyShw??u5vG1T-_kzCs}ov%8#VnKDQUM*dC!@cjs-C)719jnap6T)c5WBy-7=dS*_>!Qv>lJ@8j3j=S&vNcaC9M@KZyJYLZJFW0 z89VflF*?u*eyAw$j#B#Ag)BH`!(RPfcBzQ7mxj5P#1~cCG1`Y2!>PoBUZCYPnMmcx z;ElzN`Xc$Km(U|}Dg0GR@~zk0X$Gd8J^G=|II>pBU*89WQoO~SjjxZ+XNpe7=Xj{Q zZXNNFDJxn0R6kM14Yhj>eJW*cE9O;>oHH|VS+A9s9=vKkO#k86gu!-MX&*v<8q5G8 zDNZY~%{-)|SvP@+y2{ow)}W zu6=$3GzOi;Y_A+*?7s|1R=dWWkve+qHQ@=j5)>8^nRVzi{;cou-*=509~UddV*6Oe zQpzh*kq5tgaoF_J2=>?w3m{dcGj0~v(|oWo7qYeAHna%=GA+tvLobVDFG=V-b~-Cs zI13LxewrC*i5Gg(Zq1R|md9~Vuw9d+mwIdy!5Tp?7$$0NerI;g_0im#L-9mEr*!fB zUv5)ZlV<~MA`d2WcE9c znB<)_d{E{KE|qiir@)MJI&_b|gK2^@W{GG+DT5t;w!!6;4>O{XUp(Tis}AS)NYTiPr+i4qG#}18Ru)|Tn8mFe z^WKaV{*nv1%hkGzX1f>kJ2U!cFARFRmy*-SWeYJtfd@rnYnGWfR`G51oL-PBdog4j zOs(gx2R<)r;(f1xriPV=>MXJ8i5=@PkIti;gHa@L7~glmX3x||*DS`TQ8KMmCbYf) z*DuSD*tal98WmjSom0WUTV?sK1AQ8ti!QmzAU?^B%%e4K9+zFr`16ciu1clpx6Ug8 z?>LvAQE;I%g&@L1&NhA&r+5##L2fjiZGu>?SK+GrJ={^p%4vq`i;qumMT|Rq{~Q!M z7~PA0-ZM?xbFdn2*^62ns$~q^*Ft;3MMtO2$Rn>%&_R|<3+0z$y>zL_9LLVGkyD~6 z%8Z7;$=gV}&OiUACaPCXH0xq--Nd7LgUphk`Lg55i66G(W_6*f1 zdah0)+Z7zFmMA!AA4g|dJB$*F!RYb2xOvdSFCK3DckwHl%Bpq^_ORI(ZX)_Gb-FNJ zZrbvQfS-RRJQSTEGL9A^hJl}BLL9&?%__eha!f!5^~P>kvhtF$%Ku8;+U;}u zV@`!u?Dh|}JDGRdoxhT1W4@Y@2{|{_DSmKxk$-;x9x3Zj@qL09`LSH|@#IIY;eM6W zBAyxUEteIs26~ID!5GnJH87O!#QiJ6^cD0V=}TkS$;*TeD$&3`Y7c>i{ z%jqf8#jANcfA?OY_^&0;8nfR)I#J*lTy(Rg{pauP7Q+U1(#3u%)m4;|8a*BN zE{bIM?VIbl6%g)4>_LTwgvJZuCL||76Oj0|_n`6BANl4^KAkG{=bw(VHrA!IubNcR zpH7dIu^0SwSI_>2^bKCQ8p)4y8(ZXGEQ}iLiQ~~ByF3>bp;7RVkX%$`O-ge&V>#j) zE+Q9kuVJMOT|mD zzu;;Z%I6DjwlAVxGcn>s&6K&LsG85{XOTL2PY)XQSIw8d&&C%tFtgZ)2}+GR?avSs z-Q4~>TKoHmVk%37VMT#MjX7Bb!qC|119=NY?C5l<5;dq3Z#v%Af1|k|ovEKZ>sbG# zE|N8Mozbm>CLo$In(6PdX}ac5v>K%8uiG%<*S1btd&G2g_1NBIS9ZpCtL`U9nGom9 zxcr4?Tyd&X&Er)c*``0zm{;_G7bLod7@gD9KA51P3ATZK`i`z(n=oWj};;Hc5qmCc=isKkHU2b25YX#o(TZ~@yek;zMR1R7&IO^)@shgTU zsck5A`O{9O7vwX`Q-07}q|Hw*ccSeY*X}y7dk&|JP^E+qOrJ>gLY`B-+Nu6g4t&!G; z@mJ&)twblv+Xk;s6pQVi05ab?O09Luyg!w(t}AEqSx(hOOpv(8sl?fRvLdkDGACcP zecF*WRx6FBS<7O+a6uO}x}n^+I{vAJ8vb?P?#sSx16F_J3QlQjxn{$%r<=2rvmtfX zoj{G^&YM!so}sK^F@bUQkCE7J-Bs@e?Q0tzk7YK9S`|!JIA7bS)6U67J#efRwk~I% zLF(xma&6*qeOkOV$y+oRnjJOfv12+*{JTV}r)55qWs>vaM8hb7EHmkxEMaEG+z;;Y za@Cw>Wzt(vncE;;!*T}w-BW7bBA0l#=DPE<6#mO4`^dLn&07;x)PjX6WTDb!sNqKh zYazYJ1fS`ph0h4yWM8};bdQ&%F!XyV=PThJG)4b1t*qkBa(cmIG^a=kbyvwJUrO_D zgH5`8F{tqckx?^#WAHP&BD<&#w#5>_1A-=cASEdS`9r3Hn?j7DgkNWGG|y`4hhGpB zdLDVU?te`vjk)ldPBhHVHG)s^YJK!!>w^gCWAsqJA*OFp;HzUT7^Tq;CRZE&;l}B` z>s8$PqwM?@o|vXwe!T~?L+i%z6JWbwR_sxvtXjsvB91wxDAm;`cW=KeCfbThc&&Mw z+w4u#k-1swXrdkqv4}&bKblPPp4zwG&Z;2lTb|z6>1+KtiWyfUFry)*VtC>}M8<2+ zGJ0_?4bSMY8d^e1kANWLut+3XOw=!CUj}k>Msp{sgvT0i=kTPx?cL!iN)!oM%Ti#=7XG{efb z9?m94@5P)EZDp8?_o<*+ZM62XdNdxvQXPu-BB_hI@QiH`l&$^6gEcwBy8GI%_0_! zUDoBuvpPW*iEuItijNLeN0=MqUW7B>_towk8)t>)Qrud<&&D{JKM|}oO3HlU+LL&S z{mg<;utN?7LwK9eUs8?)E%LgI+dpnhKod34Ls@+`V`2k%77Af{HB#_coeg-I>Jac& z>)Nk3!E@FYk6#$=1SPUeW@@ZamQj!H%_lwN?3T&AcUroeg<84~--U!xiPszoAc z3wfO}cfP@ncBMt`XzqProI+Kl`xem_mbo}tGAAQ(uUD1rE0l&5g z;(i}&OdL{KQA-{4ippK*Vg8}**TEhcOO|h{DX-qokY;9&G_@^mEYnD~v(OX}{>%+ku}{p+4V{@!~OJyS{ftk)smy1^>!&^rcd;KAMjG zHXYs4lNsJo zcPQW&+Sq!BO1Ke*qJ!CpqJwp*3}BZ0k6UB@1qwN4n0_4=_{DD2ufyq2Zk*PEiF061 zn8ee`fk#vYja?Bi24wg;H-39n-F^3PJG3RdVX#0_BfMuakEza*NX57=ntFjV?Y?Y|K6g>&ceK{M8IY+Nclt7V=sZsz|{zivD9zmPjV zrp99%4fCwzCdl$4RN|@WPo4e*JRwL=#jAcAwD|LfhCDU097*DA6Faw;6eed4Qd$^{ zHye{pOKa1HY}u_8UQU!gp?T^w?YZ`y=FD-B`bmA8GADH2$3ziTL*5@6a0lkzXRCl={C#bfpRKzf9D5Z(oTUw3UVV0WV z%*Lh(S&(S8V^zF8c-JYSt@1SaLir4QHiTX=~m$J?mrfvC)^oR61f?be;MSq38a z`g{xtTaqW?CE{OIraPDEW5yDGIHut3D2p0lW5~5EN^Uwr+NGbZ#l^)+%;{z4v&QAq){1#_j5I8{ z*rmy4XIUzL(ARY~%hn3DxF6|No5wG&6y&{;q)eeS2$H~@bx|IEq82lRRr4lMwj&Wvys01Sw5Hl_qmJ;nHH3RSwIVzNL$m&{%d3s8j@# zxy!PDs!i#abjoT@9=X@rz<-yCi@oBL9|(?WvRoxCIN zl9n)0Y!*6fNnxSXQw{}Td6($o-(eW~TP7(Bhkiy1%k@Spa?(c=B&H&qx!%dg^F z>bo7P7L@+A>a-SCLo4H%QO#}AbMtf{Ut*T2tFlq$Pf;s|F|-V_V>;tAQ=UF zg;fOZyc;=N*t&250jV&OgadSZY|!<8K8wJ36igEN2S5-F!UI491V}fKum+F;Rnf4= zkihhW+;lP^IS!mxATclsh;eoTJev%776W^XTn-q=fNmNh{J*X=MoaQ^)dpXWHh zMFLD5qJ#I>XN?8)xdFfmgBNN3y?b5*)d?_G#Kjs4hy@c+lK>+EQW9ZYkS(6So`!s& z=RqRaQI$^t;M=J zkRt+t0&IyeEQpxkUqXE{=p`cbS3v<_&ycN>lfiOcfg&VHSOzlt|PIhFgX45z{#n31b3uzv;=hQ5Kvgb`7ugtim;+-1z~34hAVx$`U|29+#2hF>=-(`X!vdHLMBD**S_ta6?uhgUz=NV+C|^NA&BbkDv=)4={m9avg;N1YKYfYdP#O3jGEY=$r*LTTp}a z?Eop|ASd7!3ef!c-x47eVC`$cAx>KX)?SDlFsT5GTA>08KtzG?q5|2>sHDJM1(<`a zDDb!vOeNO@>=2lu4^$)2%N6*Ez%5^ZuL?xHIKUHu*vUW}0>_Jhdj$G@1SG3L^z8sb z5csDL7(^i7B7j{3V(tc@jKGE8KpX-??ty6pa+3mNwIEtj0lEkr;si1gSoH*00}+Mr z1sYH#f<_7mega!f^coGQegSH}pa$Wv2TDJITviX@5P^4z09PG|v>AXi0=092dIVb4 z0lyHK(*g+9gLz6%qXFLsz+95*!CWd=fNszVX*vL)4KNnKu>tHgv12qKssSd6vdVxC zOg6y8P*_>e0pdnb=zWe3Xd#3|X>=e3A#ACj1B(dZwKh6H)dUKG=IDSSLfC$X4&)$& z^bmAl9U)9V!2lSWVUG~&3JR2GA{fA<5>Sgoj5}Ld44||bCXRBWhXEWSgnLU2fU5-* z@*FUL++{2>Afg5A2c#ej;Qj{|DKLuAilQ)pYXqq$4FeEv1qGQ63;>Q0?tWqbpAo_p z1QS4Q1BF;zOd!Swn-nl?16@5fF@X;VbH*hmFpd!Daj*b_c2FRr#sV}D!VDW003d{4 zQdqzuLQpot0;oGcp*|i9Fh&TiBUnIw2iOjrPOu$MmazcVPSE@X6&nZ$1m`^ePOz{F zG;E-v6DEw(hJg(ncEac%|7+a@zB};$<9+0eg^gtHV5aKq;ON3-;^+wQ#K4GU{)vde te;%j-*IESs!9Rih8{f>q*@Dx<&Q|p$D!99cgwzQBc|SoyO86%b`F}nOO&kCK diff --git a/src/.gitignore b/src/.gitignore index e6521214..5c9a62d2 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -174,4 +174,9 @@ Desktop.ini $RECYCLE.BIN/ # Mac desktop service store files -.DS_Store \ No newline at end of file +.DS_Store + +# Local configuration +local.settings.json +appsettings.local.json +appsettings.Testing.local.json diff --git a/src/.idea/.idea.SaaStack/.idea/inspectionProfiles/Project_Default.xml b/src/.idea/.idea.SaaStack/.idea/inspectionProfiles/Project_Default.xml index 85ed4b44..37844881 100644 --- a/src/.idea/.idea.SaaStack/.idea/inspectionProfiles/Project_Default.xml +++ b/src/.idea/.idea.SaaStack/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,14 @@ \ No newline at end of file diff --git a/src/AWSLambdas.Api.WorkerHost/HostExtensions.cs b/src/AWSLambdas.Api.WorkerHost/HostExtensions.cs index 1ad11e88..1635825f 100644 --- a/src/AWSLambdas.Api.WorkerHost/HostExtensions.cs +++ b/src/AWSLambdas.Api.WorkerHost/HostExtensions.cs @@ -3,6 +3,7 @@ using Application.Persistence.Shared.ReadModels; using Common; using Common.Configuration; +using Common.FeatureFlags; using Common.Recording; using Infrastructure.Common.Recording; using Infrastructure.Hosting.Common; @@ -24,6 +25,7 @@ public static void AddDependencies(this IServiceCollection services, IConfigurat services.AddHttpClient(); services.AddSingleton(new AspNetConfigurationSettings(configuration)); services.AddSingleton(); + services.AddSingleton(); #if TESTINGONLY services.AddSingleton(new NullCrashReporter()); diff --git a/src/AncillaryApplication.UnitTests/FeatureFlagsApplicationSpec.cs b/src/AncillaryApplication.UnitTests/FeatureFlagsApplicationSpec.cs new file mode 100644 index 00000000..3e34afca --- /dev/null +++ b/src/AncillaryApplication.UnitTests/FeatureFlagsApplicationSpec.cs @@ -0,0 +1,85 @@ +using Application.Interfaces; +using Common; +using Common.FeatureFlags; +using FluentAssertions; +using Moq; +using UnitTesting.Common; +using Xunit; + +namespace AncillaryApplication.UnitTests; + +[Trait("Category", "Unit")] +public class FeatureFlagsApplicationSpec +{ + private readonly FeatureFlagsApplication _application; + private readonly Mock _caller; + private readonly Mock _featuresService; + + public FeatureFlagsApplicationSpec() + { + var recorder = new Mock(); + _caller = new Mock(); + _caller.Setup(cc => cc.IsAuthenticated).Returns(true); + _caller.Setup(cc => cc.CallerId).Returns("acallerid"); + _caller.Setup(cc => cc.TenantId).Returns("atenantid"); + _featuresService = new Mock(); + _featuresService.Setup(fs => fs.GetFlagAsync(It.IsAny(), It.IsAny>(), + It.IsAny>(), It.IsAny())) + .ReturnsAsync(new FeatureFlag + { + Name = "aname", + IsEnabled = true + }); + _application = new FeatureFlagsApplication(recorder.Object, _featuresService.Object); + } + + [Fact] + public async Task WhenGetFeatureFlag_ThenReturns() + { + var result = + await _application.GetFeatureFlagAsync(_caller.Object, "aname", null, "auserid", CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be("aname"); + result.Value.IsEnabled.Should().BeTrue(); + _featuresService.Verify(fs => fs.GetFlagAsync(It.Is(flag => flag.Name == "aname"), Optional.None, + "auserid", It.IsAny())); + } + + [Fact] + public async Task WhenGetFeatureFlagForCaller_ThenReturns() + { + var result = + await _application.GetFeatureFlagForCallerAsync(_caller.Object, "aname", CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be("aname"); + result.Value.IsEnabled.Should().BeTrue(); + _featuresService.Verify(fs => fs.GetFlagAsync(It.Is(flag => + flag.Name == "aname" + ), "atenantid", "acallerid", It.IsAny())); + } + + [Fact] + public async Task WhenGetAllFeatureFlags_ThenReturns() + { + _featuresService.Setup(fs => fs.GetAllFlagsAsync(It.IsAny())) + .ReturnsAsync(new List + { + new() + { + Name = "aname", + IsEnabled = true + } + }); + + var result = + await _application.GetAllFeatureFlagsAsync(_caller.Object, CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Count.Should().Be(1); + result.Value[0].Name.Should().Be("aname"); + result.Value[0].IsEnabled.Should().BeTrue(); + _featuresService.Verify(fs => fs.GetAllFlagsAsync(It.IsAny())); + } +} \ No newline at end of file diff --git a/src/AncillaryApplication/FeatureFlagsApplication.cs b/src/AncillaryApplication/FeatureFlagsApplication.cs index fbfa8524..d3f3d342 100644 --- a/src/AncillaryApplication/FeatureFlagsApplication.cs +++ b/src/AncillaryApplication/FeatureFlagsApplication.cs @@ -28,8 +28,24 @@ public async Task, Error>> GetAllFeatureFlagsAsync(ICal return flags.Value.ToList(); } + public async Task> GetFeatureFlagForCallerAsync(ICallerContext context, string name, + CancellationToken cancellationToken) + { + var flag = await _featureFlags.GetFlagAsync(new Flag(name), context, cancellationToken); + if (!flag.IsSuccessful) + { + return flag.Error; + } + + _recorder.TraceInformation(context.ToCall(), + "Feature flag {Name} was retrieved for user {User} in tenant {Tenant}", name, context.CallerId, + context.TenantId ?? "none"); + + return flag.Value; + } + public async Task> GetFeatureFlagAsync(ICallerContext context, string name, - string? tenantId, string? userId, CancellationToken cancellationToken) + string? tenantId, string userId, CancellationToken cancellationToken) { var flag = await _featureFlags.GetFlagAsync(new Flag(name), tenantId, userId, cancellationToken); if (!flag.IsSuccessful) @@ -37,7 +53,7 @@ public async Task> GetFeatureFlagAsync(ICallerContext return flag.Error; } - _recorder.TraceInformation(context.ToCall(), "Feature flag {Name} was retrieved", name); + _recorder.TraceInformation(context.ToCall(), "Feature flag {Name} was retrieved for user {User}", name, userId); return flag.Value; } diff --git a/src/AncillaryApplication/IFeatureFlagsApplication.cs b/src/AncillaryApplication/IFeatureFlagsApplication.cs new file mode 100644 index 00000000..c92f9f12 --- /dev/null +++ b/src/AncillaryApplication/IFeatureFlagsApplication.cs @@ -0,0 +1,17 @@ +using Application.Interfaces; +using Common; +using Common.FeatureFlags; + +namespace AncillaryApplication; + +public interface IFeatureFlagsApplication +{ + Task, Error>> GetAllFeatureFlagsAsync(ICallerContext context, + CancellationToken cancellationToken); + + Task> GetFeatureFlagAsync(ICallerContext context, string name, string? tenantId, + string userId, CancellationToken cancellationToken); + + Task> GetFeatureFlagForCallerAsync(ICallerContext context, string name, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/AncillaryDomain.UnitTests/EmailDeliverRootSpec.cs b/src/AncillaryDomain.UnitTests/EmailDeliverRootSpec.cs index 4b99fb48..33f31260 100644 --- a/src/AncillaryDomain.UnitTests/EmailDeliverRootSpec.cs +++ b/src/AncillaryDomain.UnitTests/EmailDeliverRootSpec.cs @@ -183,7 +183,7 @@ public void WhenSucceededDelivery_ThenDelivered() root.Delivered.Should().BeNear(DateTime.UtcNow); root.Events.Last().Should().BeOfType(); } - + private static QueuedMessageId CreateMessageId() { var messageId = new MessageQueueIdFactory().Create("aqueuename"); diff --git a/src/AncillaryInfrastructure.IntegrationTests/FeatureFlagsApiSpec.cs b/src/AncillaryInfrastructure.IntegrationTests/FeatureFlagsApiSpec.cs new file mode 100644 index 00000000..2a55a976 --- /dev/null +++ b/src/AncillaryInfrastructure.IntegrationTests/FeatureFlagsApiSpec.cs @@ -0,0 +1,62 @@ +using System.Net; +using ApiHost1; +using Common.FeatureFlags; +using FluentAssertions; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using IntegrationTesting.WebApi.Common; +using IntegrationTesting.WebApi.Common.Stubs; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Task = System.Threading.Tasks.Task; + +namespace AncillaryInfrastructure.IntegrationTests; + +[Trait("Category", "Integration.Web")] +[Collection("API")] +public class FeatureFlagsApiSpec : WebApiSpec +{ + private readonly StubFeatureFlags _featureFlags; + + public FeatureFlagsApiSpec(WebApiSetup setup) : base(setup, OverrideDependencies) + { + EmptyAllRepositories(); + _featureFlags = setup.GetRequiredService().As(); + _featureFlags.Reset(); + } + + [Fact] + public async Task WhenGetAllFeatureFlags_ThenReturnsFlags() + { +#if TESTINGONLY + var request = new GetAllFeatureFlagsRequest(); + + var result = await Api.GetAsync(request, req => req.SetHMACAuth(request, "asecret")); + + result.StatusCode.Should().Be(HttpStatusCode.OK); + result.Content.Value.Flags.Count.Should().Be(0); +#endif + } + + [Fact] + public async Task WhenGetFeatureFlag_ThenReturnsFlag() + { +#if TESTINGONLY + var request = new GetFeatureFlagForCallerRequest + { + Name = Flag.TestingOnly.Name + }; + + var result = await Api.GetAsync(request, req => req.SetHMACAuth(request, "asecret")); + + result.StatusCode.Should().Be(HttpStatusCode.OK); + result.Content.Value.Flag!.Name.Should().Be(Flag.TestingOnly.Name); + _featureFlags.LastGetFlag.Should().Be(Flag.TestingOnly.Name); +#endif + } + + private static void OverrideDependencies(IServiceCollection services) + { + // nothing here yet + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs new file mode 100644 index 00000000..010dffcc --- /dev/null +++ b/src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs @@ -0,0 +1,40 @@ +using AncillaryInfrastructure.Api.FeatureFlags; +using FluentAssertions; +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using UnitTesting.Common.Validation; +using Xunit; + +namespace AncillaryInfrastructure.UnitTests.Api.FeatureFlags; + +[Trait("Category", "Unit")] +public class GetFeatureFlagForCallerRequestValidatorSpec +{ + private readonly GetFeatureFlagForCallerRequest _dto; + private readonly GetFeatureFlagForCallerRequestValidator _validator; + + public GetFeatureFlagForCallerRequestValidatorSpec() + { + _validator = new GetFeatureFlagForCallerRequestValidator(); + _dto = new GetFeatureFlagForCallerRequest + { + Name = "aname" + }; + } + + [Fact] + public void WhenAllProperties_ThenSucceeds() + { + _validator.ValidateAndThrow(_dto); + } + + [Fact] + public void WhenNameIsEmpty_ThenThrows() + { + _dto.Name = string.Empty; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.GetFeatureFlagRequestValidator_InvalidName); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagRequestValidatorSpec.cs b/src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagRequestValidatorSpec.cs new file mode 100644 index 00000000..0b64a442 --- /dev/null +++ b/src/AncillaryInfrastructure.UnitTests/Api/FeatureFlags/GetFeatureFlagRequestValidatorSpec.cs @@ -0,0 +1,90 @@ +using AncillaryInfrastructure.Api.FeatureFlags; +using Domain.Common.Identity; +using Domain.Common.ValueObjects; +using FluentAssertions; +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Moq; +using UnitTesting.Common.Validation; +using Xunit; + +namespace AncillaryInfrastructure.UnitTests.Api.FeatureFlags; + +[Trait("Category", "Unit")] +public class GetFeatureFlagRequestValidatorSpec +{ + private readonly GetFeatureFlagRequest _dto; + private readonly Mock _idFactory; + private readonly GetFeatureFlagRequestValidator _validator; + + public GetFeatureFlagRequestValidatorSpec() + { + _idFactory = new Mock(); + _idFactory.Setup(idf => idf.IsValid(It.IsAny())) + .Returns(true); + _validator = new GetFeatureFlagRequestValidator(_idFactory.Object); + _dto = new GetFeatureFlagRequest + { + Name = "aname", + UserId = "auserid" + }; + } + + [Fact] + public void WhenAllProperties_ThenSucceeds() + { + _validator.ValidateAndThrow(_dto); + } + + [Fact] + public void WhenNameIsEmpty_ThenThrows() + { + _dto.Name = string.Empty; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.GetFeatureFlagRequestValidator_InvalidName); + } + + [Fact] + public void WhenTenantIdIsEmpty_ThenSucceeds() + { + _dto.TenantId = string.Empty; + + _validator.ValidateAndThrow(_dto); + } + + [Fact] + public void WhenUserIdIsEmpty_ThenThrows() + { + _dto.UserId = string.Empty; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.GetFeatureFlagRequestValidator_InvalidUserId); + } + + [Fact] + public void WhenTenantIdIsNotValid_ThenThrows() + { + _idFactory.Setup(idf => idf.IsValid("notavalidid".ToId())) + .Returns(false); + _dto.TenantId = "notavalidid"; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.GetFeatureFlagRequestValidator_InvalidTenantId); + } + + [Fact] + public void WhenUserIdIsNotValid_ThenThrows() + { + _idFactory.Setup(idf => idf.IsValid("notavalidid".ToId())) + .Returns(false); + _dto.UserId = "notavalidid"; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.GetFeatureFlagRequestValidator_InvalidUserId); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/AncillaryModule.cs b/src/AncillaryInfrastructure/AncillaryModule.cs index a2570f1c..9adffd04 100644 --- a/src/AncillaryInfrastructure/AncillaryModule.cs +++ b/src/AncillaryInfrastructure/AncillaryModule.cs @@ -44,6 +44,7 @@ public Action RegisterServices return (_, services) => { services.RegisterUnshared(); + services.RegisterUnshared(); services.RegisterUnshared(); services.RegisterUnshared(c => new UsageMessageQueue(c.Resolve(), c.Resolve(), diff --git a/src/AncillaryInfrastructure/Api/FeatureFlags/FeatureFlagsApi.cs b/src/AncillaryInfrastructure/Api/FeatureFlags/FeatureFlagsApi.cs new file mode 100644 index 00000000..39ac5f3c --- /dev/null +++ b/src/AncillaryInfrastructure/Api/FeatureFlags/FeatureFlagsApi.cs @@ -0,0 +1,48 @@ +using AncillaryApplication; +using Common.FeatureFlags; +using Infrastructure.Interfaces; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Interfaces; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; + +namespace AncillaryInfrastructure.Api.FeatureFlags; + +public class FeatureFlagsApi : IWebApiService +{ + private readonly ICallerContextFactory _contextFactory; + private readonly IFeatureFlagsApplication _featureFlagsApplication; + + public FeatureFlagsApi(ICallerContextFactory contextFactory, IFeatureFlagsApplication featureFlagsApplication) + { + _contextFactory = contextFactory; + _featureFlagsApplication = featureFlagsApplication; + } + + public async Task> Get(GetFeatureFlagRequest request, + CancellationToken cancellationToken) + { + var flag = await _featureFlagsApplication.GetFeatureFlagAsync(_contextFactory.Create(), + request.Name, request.TenantId, request.UserId, cancellationToken); + + return () => flag.HandleApplicationResult(f => new GetFeatureFlagResponse { Flag = f }); + } + + public async Task> GetForCaller( + GetFeatureFlagForCallerRequest request, + CancellationToken cancellationToken) + { + var flag = await _featureFlagsApplication.GetFeatureFlagForCallerAsync(_contextFactory.Create(), + request.Name, cancellationToken); + + return () => flag.HandleApplicationResult(f => new GetFeatureFlagResponse { Flag = f }); + } + + public async Task, GetAllFeatureFlagsResponse>> GetAll( + GetAllFeatureFlagsRequest request, + CancellationToken cancellationToken) + { + var flags = await _featureFlagsApplication.GetAllFeatureFlagsAsync(_contextFactory.Create(), cancellationToken); + + return () => flags.HandleApplicationResult(f => new GetAllFeatureFlagsResponse { Flags = f }); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs b/src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs new file mode 100644 index 00000000..6f8cec81 --- /dev/null +++ b/src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; + +namespace AncillaryInfrastructure.Api.FeatureFlags; + +public class GetFeatureFlagForCallerRequestValidator : AbstractValidator +{ + public GetFeatureFlagForCallerRequestValidator() + { + RuleFor(req => req.Name) + .NotEmpty() + .WithMessage(Resources.GetFeatureFlagRequestValidator_InvalidName); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagRequestValidator.cs b/src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagRequestValidator.cs new file mode 100644 index 00000000..f6392fd6 --- /dev/null +++ b/src/AncillaryInfrastructure/Api/FeatureFlags/GetFeatureFlagRequestValidator.cs @@ -0,0 +1,24 @@ +using Common.Extensions; +using Domain.Common.Identity; +using FluentValidation; +using Infrastructure.Web.Api.Common.Validation; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; + +namespace AncillaryInfrastructure.Api.FeatureFlags; + +public class GetFeatureFlagRequestValidator : AbstractValidator +{ + public GetFeatureFlagRequestValidator(IIdentifierFactory idFactory) + { + RuleFor(req => req.Name) + .NotEmpty() + .WithMessage(Resources.GetFeatureFlagRequestValidator_InvalidName); + RuleFor(req => req.TenantId) + .IsEntityId(idFactory) + .When(req => req.TenantId.HasValue()) + .WithMessage(Resources.GetFeatureFlagRequestValidator_InvalidTenantId); + RuleFor(req => req.UserId) + .IsEntityId(idFactory) + .WithMessage(Resources.GetFeatureFlagRequestValidator_InvalidUserId); + } +} \ No newline at end of file diff --git a/src/AncillaryInfrastructure/Resources.Designer.cs b/src/AncillaryInfrastructure/Resources.Designer.cs index 0c2c8f94..20a1f49c 100644 --- a/src/AncillaryInfrastructure/Resources.Designer.cs +++ b/src/AncillaryInfrastructure/Resources.Designer.cs @@ -76,5 +76,32 @@ internal static string AnyRecordingEventNameValidator_InvalidEventName { return ResourceManager.GetString("AnyRecordingEventNameValidator_InvalidEventName", resourceCulture); } } + + ///

+ /// Looks up a localized string similar to The 'Name' is either missing or invalid. + /// + internal static string GetFeatureFlagRequestValidator_InvalidName { + get { + return ResourceManager.GetString("GetFeatureFlagRequestValidator_InvalidName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'TenantId' is not a valid identifier. + /// + internal static string GetFeatureFlagRequestValidator_InvalidTenantId { + get { + return ResourceManager.GetString("GetFeatureFlagRequestValidator_InvalidTenantId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'UserId' is not a valid identifier. + /// + internal static string GetFeatureFlagRequestValidator_InvalidUserId { + get { + return ResourceManager.GetString("GetFeatureFlagRequestValidator_InvalidUserId", resourceCulture); + } + } } } diff --git a/src/AncillaryInfrastructure/Resources.resx b/src/AncillaryInfrastructure/Resources.resx index 835d2865..03d1a8f9 100644 --- a/src/AncillaryInfrastructure/Resources.resx +++ b/src/AncillaryInfrastructure/Resources.resx @@ -30,4 +30,13 @@ The 'EventName' is either missing or invalid + + The 'Name' is either missing or invalid + + + The 'TenantId' is not a valid identifier + + + The 'UserId' is not a valid identifier + \ No newline at end of file diff --git a/src/ApiHost1/Properties/launchSettings.json b/src/ApiHost1/Properties/launchSettings.json index 9be669ee..cd0cea57 100644 --- a/src/ApiHost1/Properties/launchSettings.json +++ b/src/ApiHost1/Properties/launchSettings.json @@ -27,9 +27,13 @@ "ASPNETCORE_ENVIRONMENT": "Production" } }, - "ApiHandler-SourceGenerators-Development-Development": { + "Api-SourceGenerators-Development": { "commandName": "DebugRoslynComponent", "targetProject": "../ApiHost1/ApiHost1.csproj" + }, + "Common-SourceGenerators-Development": { + "commandName": "DebugRoslynComponent", + "targetProject": "../Common/Common.csproj" } } } diff --git a/src/ApiHost1/appsettings.json b/src/ApiHost1/appsettings.json index b9cdf03f..18ac6229 100644 --- a/src/ApiHost1/appsettings.json +++ b/src/ApiHost1/appsettings.json @@ -28,6 +28,10 @@ "SSOUserTokens": { "AesSecret": "V7z5SZnhHRa7z68adsvazQjeIbSiWWcR+4KuAUikhe0=::u4ErEVotb170bM8qKWyT8A==" } + }, + "Flagsmith": { + "BaseUrl": "https://localhost:5656/flagsmith/", + "EnvironmentKey": "" } }, "Hosts": { diff --git a/src/Application.Common.UnitTests/CallerContextExtensionsSpec.cs b/src/Application.Common.UnitTests/Extensions/CallerContextExtensionsSpec.cs similarity index 96% rename from src/Application.Common.UnitTests/CallerContextExtensionsSpec.cs rename to src/Application.Common.UnitTests/Extensions/CallerContextExtensionsSpec.cs index 2d6d5bd4..e842ee2e 100644 --- a/src/Application.Common.UnitTests/CallerContextExtensionsSpec.cs +++ b/src/Application.Common.UnitTests/Extensions/CallerContextExtensionsSpec.cs @@ -6,7 +6,7 @@ using Moq; using Xunit; -namespace Application.Common.UnitTests; +namespace Application.Common.UnitTests.Extensions; [Trait("Category", "Unit")] public class CallerContextExtensionsSpec diff --git a/src/Application.Common.UnitTests/Extensions/FeatureFlagExtensionsSpec.cs b/src/Application.Common.UnitTests/Extensions/FeatureFlagExtensionsSpec.cs new file mode 100644 index 00000000..76f6899f --- /dev/null +++ b/src/Application.Common.UnitTests/Extensions/FeatureFlagExtensionsSpec.cs @@ -0,0 +1,116 @@ +using Application.Common.Extensions; +using Application.Interfaces; +using Common; +using Common.FeatureFlags; +using FluentAssertions; +using Moq; +using UnitTesting.Common; +using Xunit; + +namespace Application.Common.UnitTests.Extensions; + +[Trait("Category", "Unit")] +public class FeatureFlagExtensionsSpec +{ + private readonly Mock _caller = new(); + private readonly Mock _featureFlags = new(); + + public FeatureFlagExtensionsSpec() + { + _caller.Setup(x => x.IsAuthenticated) + .Returns(true); + _caller.Setup(x => x.CallerId) + .Returns("auserid"); + _caller.Setup(x => x.TenantId) + .Returns("atenantid"); +#if TESTINGONLY + _featureFlags.Setup(ff => ff.IsEnabled(Flag.TestingOnly)) + .Returns(true); + _featureFlags.Setup(ff => ff.IsEnabled(Flag.TestingOnly, It.IsAny())) + .Returns(true); + _featureFlags.Setup(ff => ff.IsEnabled(Flag.TestingOnly, It.IsAny>(), It.IsAny())) + .Returns(true); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncAndNotAuthenticated_ThenGetsFlagForAllUsers() + { +#if TESTINGONLY + _caller.Setup(x => x.IsAuthenticated) + .Returns(false); + + var result = await _featureFlags.Object.GetFlagAsync(Flag.TestingOnly, _caller.Object, CancellationToken.None); + + result.Should().BeSuccess(); + _featureFlags.Verify(x => x.GetFlagAsync(Flag.TestingOnly, Optional.None, Optional.None, + It.IsAny())); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncAndAuthenticatedButNoTenant_ThenGetsFlagForUser() + { +#if TESTINGONLY + _caller.Setup(x => x.TenantId) + .Returns((string?)null); + + var result = await _featureFlags.Object.GetFlagAsync(Flag.TestingOnly, _caller.Object, CancellationToken.None); + + result.Should().BeSuccess(); + _featureFlags.Verify(x => + x.GetFlagAsync(Flag.TestingOnly, Optional.None, "auserid", It.IsAny())); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncAndAuthenticated_ThenGetsFlagForUser() + { +#if TESTINGONLY + var result = await _featureFlags.Object.GetFlagAsync(Flag.TestingOnly, _caller.Object, CancellationToken.None); + + result.Should().BeSuccess(); + _featureFlags.Verify(x => + x.GetFlagAsync(Flag.TestingOnly, "atenantid", "auserid", It.IsAny())); +#endif + } + + [Fact] + public void WhenIsEnabledAndNotAuthenticated_ThenIsEnabled() + { +#if TESTINGONLY + _caller.Setup(x => x.IsAuthenticated) + .Returns(false); + + var result = _featureFlags.Object.IsEnabled(Flag.TestingOnly, _caller.Object); + + result.Should().BeTrue(); + _featureFlags.Verify(x => x.IsEnabled(Flag.TestingOnly)); +#endif + } + + [Fact] + public void WhenIsEnabledAndAuthenticatedButNoTenant_ThenIsEnabled() + { +#if TESTINGONLY + _caller.Setup(x => x.TenantId) + .Returns((string?)null); + + var result = _featureFlags.Object.IsEnabled(Flag.TestingOnly, _caller.Object); + + result.Should().BeTrue(); + _featureFlags.Verify(x => x.IsEnabled(Flag.TestingOnly, "auserid")); +#endif + } + + [Fact] + public void WhenIsEnabledAndAuthenticatedAndTenant_ThenIsEnabled() + { +#if TESTINGONLY + var result = _featureFlags.Object.IsEnabled(Flag.TestingOnly, _caller.Object); + + result.Should().BeTrue(); + _featureFlags.Verify(x => x.IsEnabled(Flag.TestingOnly, "atenantid", "auserid")); +#endif + } +} \ No newline at end of file diff --git a/src/Application.Common/Extensions/FeatureFlagsExtensions.cs b/src/Application.Common/Extensions/FeatureFlagsExtensions.cs new file mode 100644 index 00000000..108c4dea --- /dev/null +++ b/src/Application.Common/Extensions/FeatureFlagsExtensions.cs @@ -0,0 +1,47 @@ +using Application.Interfaces; +using Common; +using Common.Extensions; +using Common.FeatureFlags; + +namespace Application.Common.Extensions; + +public static class FeatureFlagsExtensions +{ + /// + /// Returns the specified feature for the + /// + public static async Task> GetFlagAsync(this IFeatureFlags featureFlags, Flag flag, + ICallerContext caller, CancellationToken cancellationToken) + { + if (!caller.IsAuthenticated) + { + return await featureFlags.GetFlagAsync(flag, Optional.None, Optional.None, + cancellationToken); + } + + if (caller.TenantId.HasValue()) + { + return await featureFlags.GetFlagAsync(flag, caller.TenantId, caller.CallerId, cancellationToken); + } + + return await featureFlags.GetFlagAsync(flag, Optional.None, caller.CallerId, cancellationToken); + } + + /// + /// Whether the specified feature is enabled for the + /// + public static bool IsEnabled(this IFeatureFlags featureFlags, Flag flag, ICallerContext caller) + { + if (!caller.IsAuthenticated) + { + return featureFlags.IsEnabled(flag); + } + + if (caller.TenantId.HasValue()) + { + return featureFlags.IsEnabled(flag, caller.TenantId, caller.CallerId); + } + + return featureFlags.IsEnabled(flag, caller.CallerId); + } +} \ No newline at end of file diff --git a/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj b/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj index 9a883f53..b01763ee 100644 --- a/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj +++ b/src/AzureFunctions.Api.WorkerHost/AzureFunctions.Api.WorkerHost.csproj @@ -38,6 +38,7 @@ PreserveNewest + Never diff --git a/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs b/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs index b648033c..877a47ef 100644 --- a/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs +++ b/src/AzureFunctions.Api.WorkerHost/HostExtensions.cs @@ -3,6 +3,7 @@ using Application.Persistence.Shared.ReadModels; using Common; using Common.Configuration; +using Common.FeatureFlags; using Common.Recording; using Infrastructure.Common.Recording; using Infrastructure.Hosting.Common; @@ -27,6 +28,7 @@ public static void AddDependencies(this IServiceCollection services, HostBuilder services.AddHttpClient(); services.AddSingleton(new AspNetConfigurationSettings(context.Configuration)); services.AddSingleton(); + services.AddSingleton(); #if TESTINGONLY services.AddSingleton(new NullCrashReporter()); diff --git a/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs b/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs index a5cbb39e..7677d71e 100644 --- a/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs +++ b/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs @@ -443,6 +443,118 @@ public void WhenToTitleCaseWithWords_ThenCases() result.Should().Be("Aword1 Aword2 Aword3"); } + [Fact] + public void WhenToTitleCaseWithConcatenatedWords_ThenCases() + { + var result = "AwordAword2Aword3".ToTitleCase(); + + result.Should().Be("Awordaword2aword3"); + } + + [Fact] + public void WhenToTitleCaseWithTitleCased_ThenCases() + { + var result = "Awordaword2aword3".ToTitleCase(); + + result.Should().Be("Awordaword2aword3"); + } + + [Fact] + public void WhenToCamelCaseWithSingleLowercasedWord_ThenCases() + { + var result = "aword".ToCamelCase(); + + result.Should().Be("aword"); + } + + [Fact] + public void WhenToCamelCaseWithSingleTitleCasedWord_ThenCases() + { + var result = "Aword".ToCamelCase(); + + result.Should().Be("aword"); + } + + [Fact] + public void WhenToCamelCaseWithLowercasedWords_ThenCases() + { + var result = "aword aword2 aword3".ToCamelCase(); + + result.Should().Be("awordaword2aword3"); + } + + [Fact] + public void WhenToCamelCaseWithTitleCasedWords_ThenCases() + { + var result = "Aword Aword2 Aword3".ToCamelCase(); + + result.Should().Be("awordAword2Aword3"); + } + + [Fact] + public void WhenToCamelCaseWithConcatenatedWords_ThenCases() + { + var result = "AwordAword2Aword3".ToCamelCase(); + + result.Should().Be("awordAword2Aword3"); + } + + [Fact] + public void WhenToCamelCaseWithCamelcased_ThenCases() + { + var result = "awordAword2Aword3".ToCamelCase(); + + result.Should().Be("awordAword2Aword3"); + } + + [Fact] + public void WhenToSnakeCaseWithSingleLowercasedWord_ThenCases() + { + var result = "aword".ToSnakeCase(); + + result.Should().Be("aword"); + } + + [Fact] + public void WhenToSnakeCaseWithSingleTitleCasedWord_ThenCases() + { + var result = "Aword".ToSnakeCase(); + + result.Should().Be("aword"); + } + + [Fact] + public void WhenToSnakeCaseWithLowercasedWords_ThenCases() + { + var result = "aword aword2 aword3".ToSnakeCase(); + + result.Should().Be("aword_aword2_aword3"); + } + + [Fact] + public void WhenToSnakeCaseWithTitleCasedWords_ThenCases() + { + var result = "Aword Aword2 Aword3".ToSnakeCase(); + + result.Should().Be("aword_aword2_aword3"); + } + + [Fact] + public void WhenToSnakeCaseWithConcatenatedWords_ThenCases() + { + var result = "AwordAword2Aword3".ToSnakeCase(); + + result.Should().Be("aword_aword2_aword3"); + } + + [Fact] + public void WhenToSnakeCaseWithSnakeCased_ThenCases() + { + var result = "aword_aword2_aword3".ToSnakeCase(); + + result.Should().Be("aword_aword2_aword3"); + } + private class SerializableClass { public string? AProperty { get; set; } diff --git a/src/Common/Common.csproj b/src/Common/Common.csproj index 0941d7aa..9dec4a9c 100644 --- a/src/Common/Common.csproj +++ b/src/Common/Common.csproj @@ -3,7 +3,10 @@ net7.0 true - COMMON_PROJECT + $(DefineConstants);COMMON_PROJECT + + true + Generated @@ -19,11 +22,23 @@ + + + + + + + + ResXFileCodeGenerator Resources.Designer.cs + + ResXFileCodeGenerator + FeatureFlags.Designer.cs + @@ -31,6 +46,11 @@ True Resources.resx + + True + True + FeatureFlags.resx + diff --git a/src/Common/Extensions/StringExtensions.cs b/src/Common/Extensions/StringExtensions.cs index 01c48c4e..ba36d420 100644 --- a/src/Common/Extensions/StringExtensions.cs +++ b/src/Common/Extensions/StringExtensions.cs @@ -1,15 +1,20 @@ -using System.Diagnostics; #if COMMON_PROJECT +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using JetBrains.Annotations; + #elif GENERATORS_WEB_API_PROJECT using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; +#elif GENERATORS_COMMON_PROJECT +using System.Globalization; +using System.Text; #endif namespace Common.Extensions; @@ -127,7 +132,7 @@ public static bool HasValue([NotNullWhen(true)] this string? value) { return !string.IsNullOrEmpty(value) && !string.IsNullOrWhiteSpace(value); } -#elif GENERATORS_WEB_API_PROJECT +#elif GENERATORS_WEB_API_PROJECT || GENERATORS_COMMON_PROJECT /// /// Whether the string value contains no value: it is either: null, empty or only whitespaces /// @@ -223,7 +228,40 @@ public static bool ToBoolOrDefault(this string value, bool defaultValue) return defaultValue; } +#endif +#if COMMON_PROJECT + /// + /// Returns the specified in camelCase. i.e. first letter is lower case + /// + public static string ToCamelCase(this string value) + { + if (value.HasNoValue()) + { + return value; + } + + return JsonNamingPolicy.CamelCase + .ConvertName(value) + .Replace(" ", string.Empty); + } +#elif GENERATORS_COMMON_PROJECT + /// + /// Returns the specified in camelCase. i.e. first letter is lower case + /// + public static string ToCamelCase(this string value) + { + if (value.HasNoValue()) + { + return value; + } + var titleCase = value.ToTitleCase() + .Replace(" ", string.Empty); + + return char.ToLowerInvariant(titleCase[0]) + titleCase.Substring(1); + } +#endif +#if COMMON_PROJECT /// /// Converts the to a integer value /// @@ -307,15 +345,79 @@ public static int ToIntOrDefault(this string? value, int defaultValue) return result; } #endif -#if COMMON_PROJECT +#if COMMON_PROJECT || GENERATORS_COMMON_PROJECT + /// + /// Returns the specified in snake_case. i.e. lower case with underscores for upper cased + /// letters + /// + public static string ToSnakeCase(this string value) + { + if (value.HasNoValue()) + { + return value; + } + + value = value + .Replace(" ", "_") + .ToCamelCase(); + + var builder = new StringBuilder(); + var isFirstCharacter = true; + var lastCharWasUnderscore = false; + foreach (var charValue in value) + { + if (isFirstCharacter) + { + isFirstCharacter = false; + builder.Append(char.ToLower(charValue)); + continue; + } + + if (IsIgnoredCharacter(charValue)) + { + builder.Append(charValue); + if (charValue == '_') + { + lastCharWasUnderscore = true; + } + } + else + { + if (lastCharWasUnderscore) + { + builder.Append(char.ToLower(charValue)); + lastCharWasUnderscore = false; + } + else + { + builder.Append('_'); + builder.Append(char.ToLower(charValue)); + } + } + } + + return builder.ToString(); + + static bool IsIgnoredCharacter(char charValue) + { + return char.IsDigit(charValue) + || (char.IsLetter(charValue) && char.IsLower(charValue)) + || charValue == '_'; + } + } +#endif +#if COMMON_PROJECT || GENERATORS_COMMON_PROJECT /// /// Returns the specified in title-case. i.e. first letter of words are capitalized /// public static string ToTitleCase(this string value) { - return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(value).Replace("_", string.Empty); + return CultureInfo.InvariantCulture.TextInfo + .ToTitleCase(value) + .Replace("_", string.Empty); } - +#endif +# if COMMON_PROJECT /// /// Returns the specified including only letters (no numbers, or whitespace) /// @@ -329,6 +431,14 @@ public static string TrimNonAlpha(this string value) return value.ReplaceWith(@"[^\p{L}]", string.Empty); } + /// + /// Returns the specified without any leading slashes + /// + public static string WithoutLeadingSlash(this string path) + { + return path.TrimStart('/'); + } + /// /// Returns the specified without any trailing slashes /// @@ -336,5 +446,13 @@ public static string WithoutTrailingSlash(this string path) { return path.TrimEnd('/'); } + + /// + /// Returns the specified including a trailing slash + /// + public static string WithTrailingSlash(this string path) + { + return path.TrimEnd('/') + '/'; + } #endif } \ No newline at end of file diff --git a/src/Common/FeatureFlags/FeatureFlag.cs b/src/Common/FeatureFlags/FeatureFlag.cs new file mode 100644 index 00000000..82dddb95 --- /dev/null +++ b/src/Common/FeatureFlags/FeatureFlag.cs @@ -0,0 +1,11 @@ +namespace Common.FeatureFlags; + +/// +/// The definition of a feature flag +/// +public class FeatureFlag +{ + public bool IsEnabled { get; set; } + + public required string Name { get; set; } +} \ No newline at end of file diff --git a/src/Common/FeatureFlags/FeatureFlags.Designer.cs b/src/Common/FeatureFlags/FeatureFlags.Designer.cs new file mode 100644 index 00000000..72c5c389 --- /dev/null +++ b/src/Common/FeatureFlags/FeatureFlags.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Common.FeatureFlags { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class FeatureFlags { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FeatureFlags() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Common.FeatureFlags", typeof(FeatureFlags).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to a feature flag. + /// + internal static string AFeatureFlag { + get { + return ResourceManager.GetString("AFeatureFlag", resourceCulture); + } + } + } +} diff --git a/src/Common/FeatureFlags/FeatureFlags.resx b/src/Common/FeatureFlags/FeatureFlags.resx new file mode 100644 index 00000000..4edcaafc --- /dev/null +++ b/src/Common/FeatureFlags/FeatureFlags.resx @@ -0,0 +1,29 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + a feature flag + + \ No newline at end of file diff --git a/src/Common/FeatureFlags/Flag.cs b/src/Common/FeatureFlags/Flag.cs new file mode 100644 index 00000000..ebdd46eb --- /dev/null +++ b/src/Common/FeatureFlags/Flag.cs @@ -0,0 +1,24 @@ +namespace Common.FeatureFlags; + +/// +/// Defines a feature flag. +/// New feature flag values should be added to the file, +/// and they will be source generated into into this class at build time +/// +#if GENERATORS_COMMON_PROJECT +public class Flag +#else +public partial class Flag +#endif +{ +#if TESTINGONLY + public static readonly Flag TestingOnly = new("testingonly"); +#endif + + public Flag(string name) + { + Name = name; + } + + public string Name { get; } +} \ No newline at end of file diff --git a/src/Common/FeatureFlags/IFeatureFlags.cs b/src/Common/FeatureFlags/IFeatureFlags.cs new file mode 100644 index 00000000..e1683279 --- /dev/null +++ b/src/Common/FeatureFlags/IFeatureFlags.cs @@ -0,0 +1,35 @@ +namespace Common.FeatureFlags; + +/// +/// Defines a service that provides feature flags +/// +public interface IFeatureFlags +{ + /// + /// Returns all available feature flags + /// + /// + Task, Error>> GetAllFlagsAsync(CancellationToken cancellationToken); + + /// + /// Returns the feature flag and its state + /// + Task> GetFlagAsync(Flag flag, Optional tenantId, Optional userId, + CancellationToken cancellationToken); + + /// + /// Whether the is enabled + /// + bool IsEnabled(Flag flag); + + /// + /// Whether the is enabled for the specified + /// + bool IsEnabled(Flag flag, string userId); + + /// + /// Whether the is enabled for the specified of the + /// specified in that tenant. + /// + bool IsEnabled(Flag flag, Optional tenantId, string userId); +} \ No newline at end of file diff --git a/src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/FeatureFlags/Flag.g.cs b/src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/FeatureFlags/Flag.g.cs new file mode 100644 index 00000000..106a0a44 --- /dev/null +++ b/src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/FeatureFlags/Flag.g.cs @@ -0,0 +1,11 @@ +// +using Common.FeatureFlags; + +namespace Common.FeatureFlags; + +/// +partial class Flag +{ + public static Flag AFeatureFlag = new Flag("a_feature_flag"); + +} diff --git a/src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/Flag.g.cs b/src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/Flag.g.cs new file mode 100644 index 00000000..5cdde53c --- /dev/null +++ b/src/Common/Generated/Tools.Generators.Common/Tools.Generators.Common.FeatureFlagGenerator/Flag.g.cs @@ -0,0 +1,11 @@ +// +using Common; + +namespace Common; + +/// +partial class Flag +{ + public static Flag AFeatureFlag = new Flag("a_feature_flag"); + +} diff --git a/src/Infrastructure.Hosting.Common/EmptyFeatureFlags.cs b/src/Infrastructure.Hosting.Common/EmptyFeatureFlags.cs new file mode 100644 index 00000000..ee507a7e --- /dev/null +++ b/src/Infrastructure.Hosting.Common/EmptyFeatureFlags.cs @@ -0,0 +1,41 @@ +using Common; +using Common.FeatureFlags; + +namespace Infrastructure.Hosting.Common; + +/// +/// Provides a that has no feature flags, and all are enabled +/// +public class EmptyFeatureFlags : IFeatureFlags +{ + public Task, Error>> GetAllFlagsAsync(CancellationToken cancellationToken) + { + return Task.FromResult, Error>>(new List()); + } + + public async Task> GetFlagAsync(Flag flag, Optional tenantId, + Optional userId, CancellationToken cancellationToken) + { + await Task.CompletedTask; + return new Result(new FeatureFlag + { + Name = flag.Name, + IsEnabled = true + }); + } + + public bool IsEnabled(Flag flag) + { + return true; + } + + public bool IsEnabled(Flag flag, string userId) + { + return true; + } + + public bool IsEnabled(Flag flag, Optional tenantId, string userId) + { + return true; + } +} \ No newline at end of file diff --git a/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj b/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj index cef14330..b0cec146 100644 --- a/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj +++ b/src/Infrastructure.Hosting.Common/Infrastructure.Hosting.Common.csproj @@ -36,8 +36,4 @@ - - - - diff --git a/src/Infrastructure.Shared.IntegrationTests/ApplicationServices/External/FlagsmithHttpServiceClientSpec.cs b/src/Infrastructure.Shared.IntegrationTests/ApplicationServices/External/FlagsmithHttpServiceClientSpec.cs new file mode 100644 index 00000000..7407bb19 --- /dev/null +++ b/src/Infrastructure.Shared.IntegrationTests/ApplicationServices/External/FlagsmithHttpServiceClientSpec.cs @@ -0,0 +1,244 @@ +using Common; +using Common.Configuration; +using Common.Extensions; +using Common.FeatureFlags; +using Common.Recording; +using Domain.Interfaces; +using FluentAssertions; +using Infrastructure.Shared.ApplicationServices.External; +using IntegrationTesting.WebApi.Common; +using Microsoft.Extensions.DependencyInjection; +using UnitTesting.Common; +using Xunit; + +namespace Infrastructure.Shared.IntegrationTests.ApplicationServices.External; + +[Trait("Category", "Integration.External")] +[Collection("External")] +public class FlagsmithHttpServiceClientSpec : ExternalApiSpec +{ + private const string TestTenant1 = "atenant1"; + private const string TestTenant2 = "atenant2"; + private const string TestUser1 = "auser1"; + private const string TestUser2 = "auser2"; + private static bool _isInitialized; + private readonly FlagsmithHttpServiceClient _serviceClient; + + public FlagsmithHttpServiceClientSpec(ExternalApiSetup setup) : base(setup, OverrideDependencies) + { + var settings = setup.GetRequiredService(); + _serviceClient = new FlagsmithHttpServiceClient(NullRecorder.Instance, settings, new TestHttpClientFactory()); + if (!_isInitialized) + { + _isInitialized = true; + SetupEnvironmentAsync().GetAwaiter().GetResult(); + } + } + + [Fact] + public async Task WhenGetAllFlags_ThenReturnsFlags() + { +#if TESTINGONLY + var result = await _serviceClient.GetAllFlagsAsync(); + + result.Should().BeSuccess(); + result.Value.Count.Should().Be(1); + result.Value[0].Name.Should().Be(Flag.TestingOnly.Name); + result.Value[0].IsEnabled.Should().BeFalse(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForAnUnknownFeature_ThenReturnsError() + { + var result = await _serviceClient.GetFlagAsync(new Flag("unknown"), Optional.None, + Optional.None, CancellationToken.None); + + result.Should().BeError(ErrorCode.EntityNotFound, + Resources.FlagsmithHttpServiceClient_UnknownFeature.Format("unknown")); + } + + [Fact] + public async Task WhenGetFlagAsyncForKnownFeatureWithNoOverrides_ThenReturnsDefaultFlag() + { +#if TESTINGONLY + var result = await _serviceClient.GetFlagAsync(Flag.TestingOnly, Optional.None, Optional.None, + CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeFalse(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForNoUserIdentity_ThenReturnsDefaultFlag() + { +#if TESTINGONLY + var result = await _serviceClient.GetFlagAsync(Flag.TestingOnly, "anunknowntenantid", Optional.None, + CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeFalse(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForAnUnknownUserIdentity_ThenReturnsDefaultFlag() + { +#if TESTINGONLY + var result = await _serviceClient.GetFlagAsync(Flag.TestingOnly, Optional.None, "anunknownuserid", + CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeFalse(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForAnonymousUserIdentity_ThenReturnsDefaultFlag() + { +#if TESTINGONLY + var result = await _serviceClient.GetFlagAsync(Flag.TestingOnly, Optional.None, + CallerConstants.AnonymousUserId, + CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeFalse(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForAOverriddenUserIdentity_ThenReturnsOverriddenFlag() + { +#if TESTINGONLY + var result = await _serviceClient.GetFlagAsync(Flag.TestingOnly, Optional.None, TestUser1, + CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeTrue(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForAnUnknownMembershipIdentity_ThenReturnsDefaultFlag() + { +#if TESTINGONLY + var result = + await _serviceClient.GetFlagAsync(Flag.TestingOnly, TestTenant2, TestUser2, CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeFalse(); +#endif + } + + [Fact] + public async Task WhenGetFlagAsyncForAOverriddenMembershipIdentity_ThenReturnsOverriddenFlag() + { +#if TESTINGONLY + var result = + await _serviceClient.GetFlagAsync(Flag.TestingOnly, TestTenant1, TestUser1, CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be(Flag.TestingOnly.Name); + result.Value.IsEnabled.Should().BeTrue(); +#endif + } + + [Fact] + public void WhenIsEnabledForUnknownFeature_ThenReturnsFalse() + { + var result = _serviceClient.IsEnabled(new Flag("unknown")); + + result.Should().BeFalse(); + } + + [Fact] + public void WhenIsEnabledForUnknownFeatureWithNoOverrides_ThenReturnsFalse() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly); + + result.Should().BeFalse(); +#endif + } + + [Fact] + public void WhenIsEnabledForNoIdentity_ThenReturnsFalse() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly, "anunknowntenantid", Optional.None); + + result.Should().BeFalse(); +#endif + } + + [Fact] + public void WhenIsEnabledForUnknownIdentity_ThenReturnsFalse() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly, "anunknownuserid"); + + result.Should().BeFalse(); +#endif + } + + [Fact] + public void WhenIsEnabledForAnonymousUserIdentity_ThenReturnsFalse() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly, CallerConstants.AnonymousUserId); + + result.Should().BeFalse(); +#endif + } + + [Fact] + public void WhenIsEnabledForOverriddenIdentity_ThenReturnsTrue() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly, TestUser1); + + result.Should().BeTrue(); +#endif + } + + [Fact] + public void WhenIsEnabledForUnknownMembershipIdentity_ThenReturnsFalse() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly, TestTenant2, TestUser2); + + result.Should().BeFalse(); +#endif + } + + [Fact] + public void WhenIsEnabledForOverriddenMembershipIdentity_ThenReturnsTrue() + { +#if TESTINGONLY + var result = _serviceClient.IsEnabled(Flag.TestingOnly, TestTenant1, TestUser1); + + result.Should().BeTrue(); +#endif + } + + private static void OverrideDependencies(IServiceCollection services) + { + } + + private async Task SetupEnvironmentAsync() + { +#if TESTINGONLY + await _serviceClient.DestroyAllFeaturesAsync(); + await _serviceClient.DestroyAllIdentitiesAsync(); + await _serviceClient.CreateFeatureAsync(Flag.TestingOnly, false); + await _serviceClient.CreateIdentityAsync(TestUser1, Flag.TestingOnly, true); +#endif + } +} \ No newline at end of file diff --git a/src/Infrastructure.Shared.IntegrationTests/Infrastructure.Shared.IntegrationTests.csproj b/src/Infrastructure.Shared.IntegrationTests/Infrastructure.Shared.IntegrationTests.csproj new file mode 100644 index 00000000..9fcd56b8 --- /dev/null +++ b/src/Infrastructure.Shared.IntegrationTests/Infrastructure.Shared.IntegrationTests.csproj @@ -0,0 +1,27 @@ + + + + net7.0 + true + + + + + + + + + + + + + + Always + + + Always + Never + + + + diff --git a/src/Infrastructure.Shared.IntegrationTests/TestHttpClientFactory.cs b/src/Infrastructure.Shared.IntegrationTests/TestHttpClientFactory.cs new file mode 100644 index 00000000..14fc03c2 --- /dev/null +++ b/src/Infrastructure.Shared.IntegrationTests/TestHttpClientFactory.cs @@ -0,0 +1,9 @@ +namespace Infrastructure.Shared.IntegrationTests; + +public class TestHttpClientFactory : IHttpClientFactory +{ + public HttpClient CreateClient(string name) + { + return new HttpClient(); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Shared.IntegrationTests/appsettings.Testing.json b/src/Infrastructure.Shared.IntegrationTests/appsettings.Testing.json new file mode 100644 index 00000000..457919ed --- /dev/null +++ b/src/Infrastructure.Shared.IntegrationTests/appsettings.Testing.json @@ -0,0 +1,26 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "ApplicationServices": { + "Persistence": { + "LocalMachineJsonFileStore": { + "RootPath": "./saastack/testing/external" + } + }, + "Flagsmith": { + "BaseUrl": "https://edge.api.flagsmith.com/api/v1/", + "EnvironmentKey": "", + "TestingOnly": { + "BaseUrl": "https://api.flagsmith.com/api/v1/", + "ApiToken": "", + "ProjectId": 0, + "EnvironmentApiKey": "" + } + } + } +} \ No newline at end of file diff --git a/src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.TestingOnly.cs b/src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.TestingOnly.cs new file mode 100644 index 00000000..59d68bcf --- /dev/null +++ b/src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.TestingOnly.cs @@ -0,0 +1,137 @@ +#if TESTINGONLY +using Infrastructure.Web.Api.Common; +using Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; +using Infrastructure.Web.Interfaces.Clients; +using Flag = Common.FeatureFlags.Flag; + +namespace Infrastructure.Shared.ApplicationServices.External; + +/// +partial class FlagsmithHttpServiceClient +{ + private const string TestingOnlyApiTokenSettingName = "ApplicationServices:Flagsmith:TestingOnly:ApiToken"; + private const string TestingOnlyEnvironmentApiKeySettingName = + "ApplicationServices:Flagsmith:TestingOnly:EnvironmentApiKey"; + private const string TestingOnlyPrivateApiUrlSettingName = "ApplicationServices:Flagsmith:TestingOnly:BaseUrl"; + private const string TestingOnlyProjectIdSettingName = "ApplicationServices:Flagsmith:TestingOnly:ProjectId"; + private readonly TestingOnlyConfiguration _testingConfiguration; + private readonly IServiceClient _testingOnlyClient; + + public async Task CreateFeatureAsync(Flag flag, bool enabled) + { + var featureCreated = await _testingOnlyClient.PostAsync(null, new FlagsmithCreateFeatureRequest + { + ProjectId = _testingConfiguration.ProjectId, + Name = flag.Name + }, req => AddApiToken(req, _testingConfiguration)); + var feature = featureCreated.Value; + + if (enabled) + { + var featureSetRetrieved = await _testingOnlyClient.GetAsync(null, new FlagsmithGetFeatureStatesRequest + { + EnvironmentApiKey = _testingConfiguration.EnvironmentApiKey, + Feature = feature.Id + }, req => AddApiToken(req, _testingConfiguration)); + + var featureStateId = featureSetRetrieved.Value.Results[0].Id; + await _testingOnlyClient.PatchAsync(null, new FlagsmithCreateFeatureStateRequest + { + EnvironmentApiKey = _testingConfiguration.EnvironmentApiKey, + FeatureStateId = featureStateId, + Feature = feature.Id, + Enabled = enabled + }, req => AddApiToken(req, _testingConfiguration)); + } + } + + public async Task CreateIdentityAsync(string name, Flag flag, bool enabled) + { + var identityCreated = await _testingOnlyClient.PostAsync(null, new FlagsmithCreateEdgeIdentityRequest + { + EnvironmentApiKey = _testingConfiguration.EnvironmentApiKey, + Identifier = name + }, req => AddApiToken(req, _testingConfiguration)); + + if (enabled) + { + var featuresRetrieved = await _testingOnlyClient.GetAsync(null, new FlagsmithGetFeaturesRequest + { + ProjectId = _testingConfiguration.ProjectId + }, req => AddApiToken(req, _testingConfiguration)); + + var featureId = featuresRetrieved.Value.Results.Single(feat => feat.Name == flag.Name).Id; + await _testingOnlyClient.PostAsync(null, + new FlagsmithCreateEdgeIdentityFeatureStateRequest + { + EnvironmentApiKey = _testingConfiguration.EnvironmentApiKey, + IdentityUuid = identityCreated.Value.IdentityUuid!, + Feature = featureId, + Enabled = enabled + }, req => AddApiToken(req, _testingConfiguration)); + } + } + + public async Task DestroyAllFeaturesAsync() + { + var featuresRetrieved = await _testingOnlyClient.GetAsync(null, new FlagsmithGetFeaturesRequest + { + ProjectId = _testingConfiguration.ProjectId + }, req => AddApiToken(req, _testingConfiguration)); + + var allFeatures = featuresRetrieved.Value.Results; + foreach (var feature in allFeatures) + { + await DestroyFeatureAsync(feature.Id); + } + } + + public async Task DestroyAllIdentitiesAsync() + { + var identitiesRetrieved = await _testingOnlyClient.GetAsync(null, new FlagsmithGetEdgeIdentitiesRequest + { + EnvironmentApiKey = _testingConfiguration.EnvironmentApiKey + }, req => AddApiToken(req, _testingConfiguration)); + + var allIdentities = identitiesRetrieved.Value.Results; + foreach (var identity in allIdentities) + { + await DestroyIdentityAsync(identity.IdentityUuid!); + } + } + + private async Task DestroyFeatureAsync(int featureId) + { + await _testingOnlyClient.DeleteAsync(null, new FlagsmithDeleteFeatureRequest + { + ProjectId = _testingConfiguration.ProjectId, + FeatureId = featureId + }, req => AddApiToken(req, _testingConfiguration)); + } + + private async Task DestroyIdentityAsync(string identityUuid) + { + await _testingOnlyClient.DeleteAsync(null, new FlagsmithDeleteEdgeIdentitiesRequest + { + EnvironmentApiKey = _testingConfiguration.EnvironmentApiKey, + IdentityUuid = identityUuid + }, req => AddApiToken(req, _testingConfiguration)); + } + + private static void AddApiToken(HttpRequestMessage req, TestingOnlyConfiguration configuration) + { + req.Headers.Add(HttpHeaders.Authorization, $"Token {configuration.ApiToken}"); + } + + public class TestingOnlyConfiguration + { + public required string ApiToken { get; init; } + + public required string ApiUrl { get; init; } + + public required string EnvironmentApiKey { get; init; } + + public required int ProjectId { get; init; } + } +} +#endif \ No newline at end of file diff --git a/src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.cs b/src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.cs new file mode 100644 index 00000000..d05a01ef --- /dev/null +++ b/src/Infrastructure.Shared/ApplicationServices/External/FlagsmithHttpServiceClient.cs @@ -0,0 +1,211 @@ +using System.Text.Json; +using Common; +using Common.Configuration; +using Common.Extensions; +using Common.FeatureFlags; +using Domain.Interfaces; +using Flagsmith; +using Infrastructure.Web.Common.Clients; +using Flag = Common.FeatureFlags.Flag; + +namespace Infrastructure.Shared.ApplicationServices.External; + +/// +/// Provides an adapter to the feature flagging services of FlagSmith +/// +/// Note: Flagsmith already supports caching and optimizations like LocalEvaluation to limit the number of network +/// calls made, so we don't need to implement explicit caching. +/// Note: For AWS when running this process in a Serverless environment like AWS Lambda, +/// Flagsmith's local evaluation mode is not likely to work very well since it expects to be connected to the API at +/// all times, and Lambdas will shutdown automatically. See +/// Overview +/// * When calling for a flag that does not exist, we will return +/// +/// * When calling for a flag that does not exist, we will return +/// +/// * We never want to ask for the flag for the anonymous user () +/// Flagsmith configuration: +/// 1. In flagsmith, we assume that there might be an identity for each user of interest, where its name is the ID of +/// the user, and it has a trait called 'type' with a value of "user" +/// 2. In flagsmith, we assume that for every membership to a tenant, the user will have a trait that will be called +/// the ID of the tenant and have a value of "tenant" +/// 3. When we ask for a flag for a userId, we create the identity with its respective traits +/// (if they don't already exist), and use the result. +/// +public partial class FlagsmithHttpServiceClient : IFeatureFlags +{ + private const string BaseUrlSettingName = "ApplicationServices:Flagsmith:BaseUrl"; + private const string EnvironmentKeySettingName = "ApplicationServices:Flagsmith:EnvironmentKey"; + private const bool FlagEnabledWhenNotExists = false; + private const string FlagsmithApiUrl = "https://edge.api.flagsmith.com/api/v1/"; + private const string TraitNameForIdentityType = "type"; + private const string TraitValueForTenancyMembership = "tenant"; + private const string TraitValueForUser = "user"; + private const string UnknownFeatureName = "_unknown"; + private readonly FlagsmithClient _client; + private readonly IRecorder _recorder; + + public FlagsmithHttpServiceClient(IRecorder recorder, IConfigurationSettings settings, + IHttpClientFactory httpClientFactory) + { + _recorder = recorder; + var apiUrl = settings.Platform.GetString(BaseUrlSettingName, FlagsmithApiUrl); + var environmentKey = settings.Platform.GetString(EnvironmentKeySettingName); +#if TESTINGONLY + _testingConfiguration = new TestingOnlyConfiguration + { + ApiUrl = settings.Platform.GetString(TestingOnlyPrivateApiUrlSettingName, string.Empty), + ApiToken = settings.Platform.GetString(TestingOnlyApiTokenSettingName, string.Empty), + ProjectId = (int)settings.Platform.GetNumber(TestingOnlyProjectIdSettingName, 0), + EnvironmentApiKey = settings.Platform.GetString(TestingOnlyEnvironmentApiKeySettingName, string.Empty) + }; +#endif + var httpClient = httpClientFactory.CreateClient("Flagsmith"); + _client = new FlagsmithClient(new FlagsmithConfiguration + { + EnvironmentKey = environmentKey, + ApiUrl = apiUrl, + Retries = 1, + CacheConfig = new CacheConfig(true) + { + DurationInMinutes = 5 + }, +#if TESTINGONLY || HOSTEDONAWS + EnableClientSideEvaluation = false, +#elif HOSTEDONAZURE + EnableClientSideEvaluation = true, +#endif + DefaultFlagHandler = _ => new Flagsmith.Flag(new Feature(UnknownFeatureName, -1), false, null, -1) + }, httpClient); +#if TESTINGONLY + _testingOnlyClient = + new ApiServiceClient(httpClientFactory, JsonSerializerOptions.Default, _testingConfiguration.ApiUrl); +#endif + } + + public async Task, Error>> GetAllFlagsAsync( + CancellationToken cancellationToken = default) + { + var environmentFlags = await _client.GetEnvironmentFlags(); + var allFlags = environmentFlags!.AllFlags().Select(flag => new FeatureFlag + { + Name = flag.GetFeatureName(), + IsEnabled = flag.Enabled + }) + .ToList(); + + _recorder.TraceInformation(null, "Fetched all feature flags from FlagSmith API"); + return allFlags; + } + + public async Task> GetFlagAsync(Flag flag, Optional tenantId, + Optional userId, CancellationToken cancellationToken) + { + IFlags? featureFlags; + if (userId.HasValue && userId != CallerConstants.AnonymousUserId) + { + if (tenantId.HasValue) + { + featureFlags = await QueryForUserMembershipAsync(tenantId, userId, cancellationToken); + } + else + { + featureFlags = await QueryForUserAsync(userId, cancellationToken); + } + } + else + { + featureFlags = await _client.GetEnvironmentFlags(); + } + + var featureFlag = await featureFlags.GetFlag(flag.Name); + if (IsDefaultFeatureFlag(featureFlag)) + { + return Error.EntityNotFound(Resources.FlagsmithHttpServiceClient_UnknownFeature.Format(flag.Name)); + } + + _recorder.TraceInformation(null, "Fetched feature flag for {Name}, for {User} from FlagSmith API", + flag.Name, userId.HasValue + ? userId + : "allusers"); + + return new FeatureFlag + { + Name = featureFlag.GetFeatureName(), + IsEnabled = featureFlag.Enabled + }; + } + + public bool IsEnabled(Flag flag) + { + var featureFlag = GetFlagAsync(flag, Optional.None, Optional.None, CancellationToken.None) + .GetAwaiter().GetResult(); + if (!featureFlag.IsSuccessful) + { + return FlagEnabledWhenNotExists; + } + + return featureFlag.Value.IsEnabled; + } + + public bool IsEnabled(Flag flag, string userId) + { + var featureFlag = GetFlagAsync(flag, Optional.None, userId, CancellationToken.None).GetAwaiter() + .GetResult(); + if (!featureFlag.IsSuccessful) + { + return FlagEnabledWhenNotExists; + } + + return featureFlag.Value.IsEnabled; + } + + public bool IsEnabled(Flag flag, Optional tenantId, string userId) + { + var featureFlag = GetFlagAsync(flag, tenantId, userId, CancellationToken.None).GetAwaiter().GetResult(); + if (!featureFlag.IsSuccessful) + { + return FlagEnabledWhenNotExists; + } + + return featureFlag.Value.IsEnabled; + } + + private static bool IsDefaultFeatureFlag(IFlag featureFlag) + { + return featureFlag.NotExists() + || featureFlag.getFeatureId() == -1 + || featureFlag.GetFeatureName() == UnknownFeatureName; + } + + private async Task QueryForUserAsync(string userId, CancellationToken cancellationToken) + { + var traits = new List + { + new Trait(TraitNameForIdentityType, TraitValueForUser) + }; + + return await QueryIdentityFlags(userId, traits, cancellationToken); + } + + private async Task QueryForUserMembershipAsync(string tenantId, string userId, + CancellationToken cancellationToken) + { + var userTraits = new List + { + new Trait(TraitNameForIdentityType, TraitValueForUser), + new Trait(tenantId, TraitValueForTenancyMembership) + }; + + return await QueryIdentityFlags(userId, userTraits, cancellationToken); + } + + private async Task QueryIdentityFlags(string identity, List traits, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + // Note: Will create this identity in Flagsmith if it does not yet exist! + // Note: will add the traits to the identity if they do not exist! + return await _client.GetIdentityFlags(identity, traits); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Shared/ApplicationServices/OAuth2HttpServiceClient.cs b/src/Infrastructure.Shared/ApplicationServices/OAuth2HttpServiceClient.cs index 0d28c638..23485cd4 100644 --- a/src/Infrastructure.Shared/ApplicationServices/OAuth2HttpServiceClient.cs +++ b/src/Infrastructure.Shared/ApplicationServices/OAuth2HttpServiceClient.cs @@ -3,7 +3,7 @@ using Application.Resources.Shared; using Common; using Common.Extensions; -using Infrastructure.Web.Api.Operations.Shared._3rdParties; +using Infrastructure.Web.Api.Operations.Shared._3rdParties.OAuth2; using Infrastructure.Web.Interfaces.Clients; namespace Infrastructure.Shared.ApplicationServices; diff --git a/src/Infrastructure.Shared/Infrastructure.Shared.csproj b/src/Infrastructure.Shared/Infrastructure.Shared.csproj index 3475be91..528576b9 100644 --- a/src/Infrastructure.Shared/Infrastructure.Shared.csproj +++ b/src/Infrastructure.Shared/Infrastructure.Shared.csproj @@ -4,10 +4,28 @@ net7.0 + + + + + + + + + + + + + + + <_Parameter1>$(AssemblyName).UnitTests + + <_Parameter1>Infrastructure.Shared.IntegrationTests + @@ -25,13 +43,4 @@ - - - - - - - - - diff --git a/src/Infrastructure.Shared/Resources.Designer.cs b/src/Infrastructure.Shared/Resources.Designer.cs index 7e7ebbfd..543aa3d5 100644 --- a/src/Infrastructure.Shared/Resources.Designer.cs +++ b/src/Infrastructure.Shared/Resources.Designer.cs @@ -58,5 +58,14 @@ internal Resources() { resourceCulture = value; } } + + /// + /// Looks up a localized string similar to The feature '{0}' has not be defined in Flagsmith. + /// + internal static string FlagsmithHttpServiceClient_UnknownFeature { + get { + return ResourceManager.GetString("FlagsmithHttpServiceClient_UnknownFeature", resourceCulture); + } + } } } diff --git a/src/Infrastructure.Shared/Resources.resx b/src/Infrastructure.Shared/Resources.resx index 755958fe..ab22ccfe 100644 --- a/src/Infrastructure.Shared/Resources.resx +++ b/src/Infrastructure.Shared/Resources.resx @@ -24,4 +24,7 @@ PublicKeyToken=b77a5c561934e089 + + The feature '{0}' has not be defined in Flagsmith + \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Common.UnitTests/Extensions/HttpRequestExtensionsSpec.cs b/src/Infrastructure.Web.Api.Common.UnitTests/Extensions/HttpRequestExtensionsSpec.cs index 7b60515a..c7df6618 100644 --- a/src/Infrastructure.Web.Api.Common.UnitTests/Extensions/HttpRequestExtensionsSpec.cs +++ b/src/Infrastructure.Web.Api.Common.UnitTests/Extensions/HttpRequestExtensionsSpec.cs @@ -28,6 +28,20 @@ public async Task WhenVerifyHMACSignatureAsyncAndWrongSignature_ThenReturnsFalse result.Should().BeFalse(); } + [Fact] + public async Task WhenVerifyHMACSignatureAsyncAndEmptyJson_ThenReturnsTrue() + { + var body = Encoding.UTF8.GetBytes(RequestExtensions.EmptyRequestJson); + var signature = new HMACSigner(body, "asecret").Sign(); + var httpRequest = new Mock(); + httpRequest.Setup(hr => hr.Body) + .Returns(new MemoryStream(body)); + + var result = await httpRequest.Object.VerifyHMACSignatureAsync(signature, "asecret", CancellationToken.None); + + result.Should().BeTrue(); + } + [Fact] public async Task WhenVerifyHMACSignatureAsyncAndCorrectSignature_ThenReturnsTrue() { diff --git a/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs b/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs index e9ebe3ed..3e14f70b 100644 --- a/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs +++ b/src/Infrastructure.Web.Api.Common/Extensions/HttpRequestExtensions.cs @@ -228,13 +228,20 @@ public static void SetRequestId(this HttpRequestMessage message, ICallContext co } /// - /// Whether the specified HMAC signature represents the signature of the contents of the inbound request + /// Whether the specified HMAC signature represents the signature of the contents of the inbound request, + /// signed by the method /// public static async Task VerifyHMACSignatureAsync(this HttpRequest request, string signature, string secret, CancellationToken cancellationToken) { var body = await request.Body.ReadFullyAsync(cancellationToken); - request.RewindBody(); // need to do this for later middleware + request.RewindBody(); // HACK: need to do this for later middleware + + if (body.Length == 0) + { + body = Encoding.UTF8.GetBytes(RequestExtensions + .EmptyRequestJson); //HACK: we assume that an empty JSON request was signed + } var signer = new HMACSigner(body, secret); var verifier = new HMACVerifier(signer); diff --git a/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs b/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs index 354b1297..57562bae 100644 --- a/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs +++ b/src/Infrastructure.Web.Api.Common/Extensions/RequestExtensions.cs @@ -9,7 +9,7 @@ namespace Infrastructure.Web.Api.Common.Extensions; public static class RequestExtensions { - private const string EmptyRequestJson = "{}"; + public const string EmptyRequestJson = "{}"; private const char RouteSegmentDelimiter = '/'; /// diff --git a/src/Infrastructure.Web.Api.Interfaces/WebServiceAttribute.cs b/src/Infrastructure.Web.Api.Interfaces/WebServiceAttribute.cs new file mode 100644 index 00000000..969ba5b0 --- /dev/null +++ b/src/Infrastructure.Web.Api.Interfaces/WebServiceAttribute.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Infrastructure.Web.Api.Interfaces; + +/// +/// Provides a declarative way to define a web API service +/// +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public class WebServiceAttribute : Attribute +{ + public WebServiceAttribute( +#if !NETSTANDARD2_0 + [StringSyntax("Route")] +#endif + string basePath) + { + BasePath = basePath; + } + + public string BasePath { get; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityFeatureStateRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityFeatureStateRequest.cs new file mode 100644 index 00000000..de178ee1 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityFeatureStateRequest.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/environments/{EnvironmentApiKey}/edge-identities/{IdentityUuid}/edge-featurestates/", ServiceOperation.Post)] +public class + FlagsmithCreateEdgeIdentityFeatureStateRequest : IWebRequest +{ + [JsonPropertyName("enabled")] public bool Enabled { get; set; } + + public required string EnvironmentApiKey { get; set; } + + [JsonPropertyName("feature")] public int Feature { get; set; } + + public required string IdentityUuid { get; set; } +} + +public class FlagsmithCreateEdgeIdentityFeatureStateResponse : IWebResponse +{ +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityRequest.cs new file mode 100644 index 00000000..fcfa300f --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityRequest.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; +using JetBrains.Annotations; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/environments/{EnvironmentApiKey}/edge-identities/", ServiceOperation.Post)] +[UsedImplicitly] +public class FlagsmithCreateEdgeIdentityRequest : IWebRequest +{ + public required string EnvironmentApiKey { get; set; } + + [JsonPropertyName("identifier")] public required string Identifier { get; set; } + + [JsonPropertyName("traits")] public List? Traits { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityResponse.cs new file mode 100644 index 00000000..39b18dab --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateEdgeIdentityResponse.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithCreateEdgeIdentityResponse : IWebResponse +{ + [JsonPropertyName("identifier")] public string? Identifier { get; set; } + + [JsonPropertyName("identity_uuid")] public string? IdentityUuid { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureRequest.cs new file mode 100644 index 00000000..adc13db2 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureRequest.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/projects/{ProjectId}/features/", ServiceOperation.Post)] +public class FlagsmithCreateFeatureRequest : IWebRequest +{ + [JsonPropertyName("name")] public required string Name { get; set; } + + public required int ProjectId { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureResponse.cs new file mode 100644 index 00000000..1fc5f73c --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureResponse.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithCreateFeatureResponse : IWebResponse +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("name")] public string? Name { get; set; } + + [JsonPropertyName("type")] public string? Type { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateRequest.cs new file mode 100644 index 00000000..a714b4e7 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateRequest.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/environments/{EnvironmentApiKey}/featurestates/{FeatureStateId}/", ServiceOperation.PutPatch)] +public class FlagsmithCreateFeatureStateRequest : IWebRequest +{ + [JsonPropertyName("enabled")] public bool Enabled { get; set; } + + public required string EnvironmentApiKey { get; set; } + + [JsonPropertyName("feature")] public int Feature { get; set; } + + public int FeatureStateId { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateResponse.cs new file mode 100644 index 00000000..0f4c6e8a --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateFeatureStateResponse.cs @@ -0,0 +1,7 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithCreateFeatureStateResponse : IWebResponse +{ +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityRequest.cs new file mode 100644 index 00000000..886e4429 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityRequest.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; +using JetBrains.Annotations; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/identities/", ServiceOperation.Post)] +[UsedImplicitly] +public class FlagsmithCreateIdentityRequest : IWebRequest +{ + [JsonPropertyName("identifier")] public required string Identifier { get; set; } + + [JsonPropertyName("traits")] public required List Traits { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityResponse.cs new file mode 100644 index 00000000..46dd39b9 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithCreateIdentityResponse.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; +using JetBrains.Annotations; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithCreateIdentityResponse : IWebResponse +{ + [JsonPropertyName("flags")] public List Flags { get; set; } = new(); + + [JsonPropertyName("identifier")] public string? Identifier { get; set; } + + [JsonPropertyName("traits")] public List Traits { get; set; } = new(); +} + +[UsedImplicitly] +public class FlagsmithTrait +{ + [JsonPropertyName("trait_key")] public string? Key { get; set; } + + [JsonPropertyName("trait_value")] public object? Value { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteEdgeIdentitiesRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteEdgeIdentitiesRequest.cs new file mode 100644 index 00000000..ce5419ff --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteEdgeIdentitiesRequest.cs @@ -0,0 +1,11 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/environments/{EnvironmentApiKey}/edge-identities/{IdentityUuid}/", ServiceOperation.Delete)] +public class FlagsmithDeleteEdgeIdentitiesRequest : IWebRequest +{ + public required string EnvironmentApiKey { get; set; } + + public required string IdentityUuid { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteFeatureRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteFeatureRequest.cs new file mode 100644 index 00000000..dc1d677b --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithDeleteFeatureRequest.cs @@ -0,0 +1,11 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/projects/{ProjectId}/features/{FeatureId}/", ServiceOperation.Delete)] +public class FlagsmithDeleteFeatureRequest : IWebRequest +{ + public required int FeatureId { get; set; } + + public required int ProjectId { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesRequest.cs new file mode 100644 index 00000000..5c340c98 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesRequest.cs @@ -0,0 +1,9 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/environments/{EnvironmentApiKey}/edge-identities/", ServiceOperation.Get)] +public class FlagsmithGetEdgeIdentitiesRequest : IWebRequest +{ + public required string EnvironmentApiKey { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesResponse.cs new file mode 100644 index 00000000..83d6cf62 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEdgeIdentitiesResponse.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; +using JetBrains.Annotations; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithGetEdgeIdentitiesResponse : IWebResponse +{ + [JsonPropertyName("results")] public List Results { get; set; } = new(); +} + +[UsedImplicitly] +public class FlagsmithEdgeIdentity +{ + [JsonPropertyName("identifier")] public string? Identifier { get; set; } + + [JsonPropertyName("identity_uuid")] public string? IdentityUuid { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsRequest.cs new file mode 100644 index 00000000..e8c5c986 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsRequest.cs @@ -0,0 +1,10 @@ +using Infrastructure.Web.Api.Interfaces; +using JetBrains.Annotations; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/flags/", ServiceOperation.Get)] +[UsedImplicitly] +public class FlagsmithGetEnvironmentFlagsRequest : IWebRequest +{ +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsResponse.cs new file mode 100644 index 00000000..700db8d0 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetEnvironmentFlagsResponse.cs @@ -0,0 +1,34 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithGetEnvironmentFlagsResponse : List, IWebResponse +{ + public FlagsmithGetEnvironmentFlagsResponse() + { + } + + public FlagsmithGetEnvironmentFlagsResponse(List flags) : base(flags) + { + } +} + +public class FlagsmithFlag +{ + [JsonPropertyName("enabled")] public bool Enabled { get; set; } + + [JsonPropertyName("feature")] public FlagsmithFeature? Feature { get; set; } + + [JsonPropertyName("id")] public int? Id { get; set; } + + [JsonPropertyName("feature_state_value")] + public string? Value { get; set; } +} + +public class FlagsmithFeature +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("name")] public string? Name { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesRequest.cs new file mode 100644 index 00000000..1d05edcb --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesRequest.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/environments/{EnvironmentApiKey}/featurestates/", ServiceOperation.Post)] +public class FlagsmithGetFeatureStatesRequest : IWebRequest +{ + public required string EnvironmentApiKey { get; set; } + + [JsonPropertyName("feature")] public int Feature { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesResponse.cs new file mode 100644 index 00000000..06f72fad --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeatureStatesResponse.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; +using JetBrains.Annotations; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithGetFeatureStatesResponse : IWebResponse +{ + [JsonPropertyName("results")] + // ReSharper disable once CollectionNeverUpdated.Global + public List Results { get; set; } = new(); +} + +[UsedImplicitly] +public class FlagsmithFeatureState +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesRequest.cs new file mode 100644 index 00000000..4803b822 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesRequest.cs @@ -0,0 +1,9 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +[Route("/projects/{ProjectId}/features/", ServiceOperation.Get)] +public class FlagsmithGetFeaturesRequest : IWebRequest +{ + public int ProjectId { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesResponse.cs new file mode 100644 index 00000000..7311102b --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/Flagsmith/FlagsmithGetFeaturesResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +public class FlagsmithGetFeaturesResponse : IWebResponse +{ + [JsonPropertyName("results")] public List Results { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/ExchangeOAuth2CodeForTokensRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/OAuth2/ExchangeOAuth2CodeForTokensRequest.cs similarity index 90% rename from src/Infrastructure.Web.Api.Operations.Shared/3rdParties/ExchangeOAuth2CodeForTokensRequest.cs rename to src/Infrastructure.Web.Api.Operations.Shared/3rdParties/OAuth2/ExchangeOAuth2CodeForTokensRequest.cs index f490d50b..ce42f4f1 100644 --- a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/ExchangeOAuth2CodeForTokensRequest.cs +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/OAuth2/ExchangeOAuth2CodeForTokensRequest.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using Infrastructure.Web.Api.Interfaces; -namespace Infrastructure.Web.Api.Operations.Shared._3rdParties; +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.OAuth2; [Route("/auth/token", ServiceOperation.Post)] public class ExchangeOAuth2CodeForTokensRequest : UnTenantedRequest diff --git a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/ExchangeOAuth2CodeForTokensResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/OAuth2/ExchangeOAuth2CodeForTokensResponse.cs similarity index 86% rename from src/Infrastructure.Web.Api.Operations.Shared/3rdParties/ExchangeOAuth2CodeForTokensResponse.cs rename to src/Infrastructure.Web.Api.Operations.Shared/3rdParties/OAuth2/ExchangeOAuth2CodeForTokensResponse.cs index f654e26e..a5a295cb 100644 --- a/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/ExchangeOAuth2CodeForTokensResponse.cs +++ b/src/Infrastructure.Web.Api.Operations.Shared/3rdParties/OAuth2/ExchangeOAuth2CodeForTokensResponse.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using Infrastructure.Web.Api.Interfaces; -namespace Infrastructure.Web.Api.Operations.Shared._3rdParties; +namespace Infrastructure.Web.Api.Operations.Shared._3rdParties.OAuth2; public class ExchangeOAuth2CodeForTokensResponse : IWebResponse { diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsRequest.cs new file mode 100644 index 00000000..7646ae63 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsRequest.cs @@ -0,0 +1,9 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +[Route("/flags", ServiceOperation.Get, AccessType.HMAC)] +[Authorize(Roles.Platform_ServiceAccount)] +public class GetAllFeatureFlagsRequest : UnTenantedRequest +{ +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsResponse.cs new file mode 100644 index 00000000..e165cf82 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetAllFeatureFlagsResponse.cs @@ -0,0 +1,9 @@ +using Common.FeatureFlags; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +public class GetAllFeatureFlagsResponse : IWebResponse +{ + public List Flags { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagForCallerRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagForCallerRequest.cs new file mode 100644 index 00000000..2f15fa73 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagForCallerRequest.cs @@ -0,0 +1,9 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +[Route("/flags/{Name}", ServiceOperation.Get)] +public class GetFeatureFlagForCallerRequest : UnTenantedRequest +{ + public required string Name { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagRequest.cs new file mode 100644 index 00000000..94ae7f00 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagRequest.cs @@ -0,0 +1,14 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +[Route("/flags/{UserId}/{Name}", ServiceOperation.Get, AccessType.HMAC)] +[Authorize(Roles.Platform_ServiceAccount)] +public class GetFeatureFlagRequest : UnTenantedRequest +{ + public required string Name { get; set; } + + public string? TenantId { get; set; } + + public required string UserId { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagResponse.cs new file mode 100644 index 00000000..1df2a466 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/Ancillary/GetFeatureFlagResponse.cs @@ -0,0 +1,9 @@ +using Common.FeatureFlags; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.Ancillary; + +public class GetFeatureFlagResponse : IWebResponse +{ + public FeatureFlag? Flag { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsRequest.cs new file mode 100644 index 00000000..a2740555 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsRequest.cs @@ -0,0 +1,8 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; + +[Route("/flags", ServiceOperation.Get)] +public class GetAllFeatureFlagsRequest : UnTenantedRequest +{ +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsResponse.cs new file mode 100644 index 00000000..c858bd32 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetAllFeatureFlagsResponse.cs @@ -0,0 +1,9 @@ +using Common.FeatureFlags; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; + +public class GetAllFeatureFlagsResponse : IWebResponse +{ + public List Flags { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagForCallerRequest.cs b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagForCallerRequest.cs new file mode 100644 index 00000000..a239eb91 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagForCallerRequest.cs @@ -0,0 +1,9 @@ +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; + +[Route("/flags/{Name}", ServiceOperation.Get)] +public class GetFeatureFlagForCallerRequest : UnTenantedRequest +{ + public required string Name { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagResponse.cs new file mode 100644 index 00000000..4f474815 --- /dev/null +++ b/src/Infrastructure.Web.Api.Operations.Shared/BackEndForFrontEnd/GetFeatureFlagResponse.cs @@ -0,0 +1,9 @@ +using Common.FeatureFlags; +using Infrastructure.Web.Api.Interfaces; + +namespace Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; + +public class GetFeatureFlagResponse : IWebResponse +{ + public FeatureFlag? Flag { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Common/Clients/ApiServiceClient.cs b/src/Infrastructure.Web.Common/Clients/ApiServiceClient.cs index 0a79f336..6870fe87 100644 --- a/src/Infrastructure.Web.Common/Clients/ApiServiceClient.cs +++ b/src/Infrastructure.Web.Common/Clients/ApiServiceClient.cs @@ -35,7 +35,7 @@ protected ApiServiceClient(IHttpClientFactory clientFactory, JsonSerializerOptio _retryPolicy = ApiClientRetryPolicies.CreateRetryWithExponentialBackoffAndJitter(retryCount); } - public async Task> DeleteAsync(ICallerContext context, + public async Task> DeleteAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -46,7 +46,7 @@ protected ApiServiceClient(IHttpClientFactory clientFactory, JsonSerializerOptio cancellationToken ?? CancellationToken.None); } - public async Task> GetAsync(ICallerContext context, + public async Task> GetAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -57,7 +57,7 @@ public async Task> GetAsync(ICalle cancellationToken ?? CancellationToken.None); } - public async Task> PatchAsync(ICallerContext context, + public async Task> PatchAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -68,7 +68,7 @@ public async Task> PatchAsync(ICal cancellationToken ?? CancellationToken.None); } - public async Task> PostAsync(ICallerContext context, + public async Task> PostAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -79,7 +79,7 @@ public async Task> PostAsync(ICall cancellationToken ?? CancellationToken.None); } - public async Task> PutAsync(ICallerContext context, + public async Task> PutAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -90,7 +90,7 @@ public async Task> PutAsync(ICalle cancellationToken ?? CancellationToken.None); } - public async Task FireAsync(ICallerContext context, IWebRequestVoid request, + public async Task FireAsync(ICallerContext? context, IWebRequestVoid request, Action? requestFilter = null, CancellationToken? cancellationToken = null) { using var client = CreateJsonClient(context, requestFilter, out var modifiedRequestFilter); @@ -98,7 +98,7 @@ await _retryPolicy.ExecuteAsync(async ct => await client.SendOneWayAsync(request cancellationToken ?? CancellationToken.None); } - public async Task FireAsync(ICallerContext context, IWebRequest request, + public async Task FireAsync(ICallerContext? context, IWebRequest request, Action? requestFilter, CancellationToken? cancellationToken = null) where TResponse : IWebResponse { @@ -108,7 +108,7 @@ await _retryPolicy.ExecuteAsync( cancellationToken ?? CancellationToken.None); } - protected virtual JsonClient CreateJsonClient(ICallerContext context, + protected virtual JsonClient CreateJsonClient(ICallerContext? context, Action? inboundRequestFilter, out Action modifiedRequestFilter) { diff --git a/src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs b/src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs index 8fa71447..c228366a 100644 --- a/src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs +++ b/src/Infrastructure.Web.Common/Clients/InterHostServiceClient.cs @@ -23,7 +23,7 @@ public InterHostServiceClient(IHttpClientFactory clientFactory, JsonSerializerOp { } - protected override JsonClient CreateJsonClient(ICallerContext context, + protected override JsonClient CreateJsonClient(ICallerContext? context, Action? inboundRequestFilter, out Action modifiedRequestFilter) { @@ -50,13 +50,19 @@ protected override JsonClient CreateJsonClient(ICallerContext context, return client; } - private static void AddCorrelationId(HttpRequestMessage message, ICallerContext context) + private static void AddCorrelationId(HttpRequestMessage message, ICallerContext? context) { - message.SetRequestId(context.ToCall()); + if (context.Exists()) + { + message.SetRequestId(context.ToCall()); + } } - private static void AddCallerAuthorization(HttpRequestMessage message, ICallerContext context) + private static void AddCallerAuthorization(HttpRequestMessage message, ICallerContext? context) { - message.SetAuthorization(context); + if (context.Exists()) + { + message.SetAuthorization(context); + } } } \ No newline at end of file diff --git a/src/Infrastructure.Web.Common/Clients/JsonClient.cs b/src/Infrastructure.Web.Common/Clients/JsonClient.cs index c9bb93e2..61727858 100644 --- a/src/Infrastructure.Web.Common/Clients/JsonClient.cs +++ b/src/Infrastructure.Web.Common/Clients/JsonClient.cs @@ -249,14 +249,17 @@ public async Task SendOneWayAsync(IWebRequest request, Action SendRequestAsync(HttpMethod method, IWebRequest request, Action? requestFilter, CancellationToken? cancellationToken = default) { var requestUri = request.GetRequestInfo().Route; - var content = new StringContent(request.SerializeToJson(), new MediaTypeHeaderValue(HttpContentTypes.Json)); + + var content = method == HttpMethod.Post || method == HttpMethod.Put || method == HttpMethod.Patch + ? new StringContent(request.SerializeToJson(), new MediaTypeHeaderValue(HttpContentTypes.Json)) + : null; return await SendRequestAsync(method, requestUri, content, requestFilter, cancellationToken); } @@ -268,7 +271,7 @@ private async Task SendRequestAsync(HttpMethod method, stri var request = new HttpRequestMessage { Method = method, - RequestUri = new Uri(_client.BaseAddress!, requestUri), + RequestUri = new Uri(_client.BaseAddress!, requestUri.WithoutLeadingSlash()), Content = requestContent, Headers = { { HttpHeaders.Accept, HttpContentTypes.Json } } }; diff --git a/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs b/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs index 7af268e2..728ebf27 100644 --- a/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs +++ b/src/Infrastructure.Web.Hosting.Common/Extensions/HostExtensions.cs @@ -5,6 +5,7 @@ using Common; using Common.Configuration; using Common.Extensions; +using Common.FeatureFlags; using Domain.Common; using Domain.Common.Identity; using Domain.Interfaces; @@ -22,6 +23,7 @@ using Infrastructure.Interfaces; using Infrastructure.Persistence.Common.ApplicationServices; using Infrastructure.Persistence.Interfaces; +using Infrastructure.Shared.ApplicationServices.External; using Infrastructure.Web.Api.Common; using Infrastructure.Web.Api.Common.Extensions; using Infrastructure.Web.Api.Common.Validation; @@ -110,6 +112,7 @@ public static WebApplication ConfigureApiHost(this WebApplicationBuilder appBuil void RegisterSharedServices() { appBuilder.Services.AddHttpContextAccessor(); + appBuilder.Services.AddSingleton(); } void RegisterConfiguration(bool isMultiTenanted) diff --git a/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj b/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj index ca0b9edd..f5032c76 100644 --- a/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj +++ b/src/Infrastructure.Web.Hosting.Common/Infrastructure.Web.Hosting.Common.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs b/src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs index 780d12ac..30fb8ffe 100644 --- a/src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs +++ b/src/Infrastructure.Web.Interfaces/Clients/IServiceClient.cs @@ -9,26 +9,28 @@ namespace Infrastructure.Web.Interfaces.Clients; /// public interface IServiceClient : IFireAndForgetServiceClient { - Task> DeleteAsync(ICallerContext context, + Task> DeleteAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new(); - Task> GetAsync(ICallerContext context, IWebRequest request, + Task> GetAsync(ICallerContext? context, + IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new(); - Task> PatchAsync(ICallerContext context, + Task> PatchAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new(); - Task> PostAsync(ICallerContext context, + Task> PostAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new(); - Task> PutAsync(ICallerContext context, IWebRequest request, + Task> PutAsync(ICallerContext? context, + IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new(); } @@ -38,10 +40,10 @@ Task> PutAsync(ICallerContext cont /// public interface IFireAndForgetServiceClient { - Task FireAsync(ICallerContext context, IWebRequestVoid request, + Task FireAsync(ICallerContext? context, IWebRequestVoid request, Action? requestFilter = null, CancellationToken? cancellationToken = null); - Task FireAsync(ICallerContext context, IWebRequest request, + Task FireAsync(ICallerContext? context, IWebRequest request, Action? requestFilter, CancellationToken? cancellationToken = null) where TResponse : IWebResponse; } \ No newline at end of file diff --git a/src/Infrastructure.Web.Website.IntegrationTests/FeatureFlagsApiSpec.cs b/src/Infrastructure.Web.Website.IntegrationTests/FeatureFlagsApiSpec.cs new file mode 100644 index 00000000..41e3ce47 --- /dev/null +++ b/src/Infrastructure.Web.Website.IntegrationTests/FeatureFlagsApiSpec.cs @@ -0,0 +1,75 @@ +using System.Net; +using System.Net.Http.Json; +using System.Text.Json; +using Common.FeatureFlags; +using FluentAssertions; +using Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; +using Infrastructure.Web.Api.Operations.Shared.TestingOnly; +using Infrastructure.Web.Hosting.Common.Pipeline; +using IntegrationTesting.WebApi.Common; +using Microsoft.Extensions.DependencyInjection; +using WebsiteHost; +using Xunit; +using GetFeatureFlagResponse = Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd.GetFeatureFlagResponse; +using Task = System.Threading.Tasks.Task; + +namespace Infrastructure.Web.Website.IntegrationTests; + +[Trait("Category", "Integration.Web")] +[Collection("API")] +public class FeatureFlagsApiSpec : WebApiSpec +{ + private readonly CSRFMiddleware.ICSRFService _csrfService; + private readonly JsonSerializerOptions _jsonOptions; + + public FeatureFlagsApiSpec(WebApiSetup setup) : base(setup, OverrideDependencies) + { + StartupServer(); + StartupServer(); + _csrfService = setup.GetRequiredService(); +#if TESTINGONLY + HttpApi.PostEmptyJsonAsync(new DestroyAllRepositoriesRequest().MakeApiRoute(), + (msg, cookies) => msg.WithCSRF(cookies, _csrfService)).GetAwaiter() + .GetResult(); +#endif + _jsonOptions = setup.GetRequiredService(); + } + + [Fact] + public async Task WhenGetAllFeatureFlags_ThenReturnsFlags() + { +#if TESTINGONLY + var request = new GetAllFeatureFlagsRequest(); + + var result = await HttpApi.GetAsync(request.MakeApiRoute(), + (msg, cookies) => msg.WithCSRF(cookies, _csrfService)); + + result.StatusCode.Should().Be(HttpStatusCode.OK); + var flags = (await result.Content.ReadFromJsonAsync(_jsonOptions))!.Flags; + flags.Count.Should().Be(2); + flags[0].Name.Should().Be(Flag.TestingOnly.Name); + flags[1].Name.Should().Be(Flag.AFeatureFlag.Name); +#endif + } + + [Fact] + public async Task WhenGetFeatureFlag_ThenReturnsFlag() + { +#if TESTINGONLY + var request = new GetFeatureFlagForCallerRequest + { + Name = Flag.TestingOnly.Name + }; + + var result = await HttpApi.GetAsync(request.MakeApiRoute()); + + result.StatusCode.Should().Be(HttpStatusCode.OK); + var flag = (await result.Content.ReadFromJsonAsync(_jsonOptions))!.Flag!; + flag.Name.Should().Be(Flag.TestingOnly.Name); +#endif + } + + private static void OverrideDependencies(IServiceCollection services) + { + } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Website.IntegrationTests/Infrastructure.Web.Website.IntegrationTests.csproj b/src/Infrastructure.Web.Website.IntegrationTests/Infrastructure.Web.Website.IntegrationTests.csproj index ece0ef9e..2c8f1bc3 100644 --- a/src/Infrastructure.Web.Website.IntegrationTests/Infrastructure.Web.Website.IntegrationTests.csproj +++ b/src/Infrastructure.Web.Website.IntegrationTests/Infrastructure.Web.Website.IntegrationTests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Infrastructure.Web.Website.IntegrationTests/WebsiteTestingExtensions.cs b/src/Infrastructure.Web.Website.IntegrationTests/WebsiteTestingExtensions.cs index 7bb32692..924771be 100644 --- a/src/Infrastructure.Web.Website.IntegrationTests/WebsiteTestingExtensions.cs +++ b/src/Infrastructure.Web.Website.IntegrationTests/WebsiteTestingExtensions.cs @@ -138,4 +138,5 @@ public static void WithCSRF(this HttpRequestMessage message, CookieContainer coo var origin = $"{message.RequestUri.Scheme}{Uri.SchemeDelimiter}{message.RequestUri.Authority}"; message.Headers.Add(HttpHeaders.Origin, origin); } + } \ No newline at end of file diff --git a/src/Infrastructure.Web.Website.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs b/src/Infrastructure.Web.Website.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs new file mode 100644 index 00000000..d5858469 --- /dev/null +++ b/src/Infrastructure.Web.Website.UnitTests/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidatorSpec.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; +using UnitTesting.Common.Validation; +using WebsiteHost; +using WebsiteHost.Api.FeatureFlags; +using Xunit; + +namespace Infrastructure.Web.Website.UnitTests.Api.FeatureFlags; + +[Trait("Category", "Unit")] +public class GetFeatureFlagForCallerRequestValidatorSpec +{ + private readonly GetFeatureFlagForCallerRequest _dto; + private readonly GetFeatureFlagForCallerRequestValidator _validator; + + public GetFeatureFlagForCallerRequestValidatorSpec() + { + _validator = new GetFeatureFlagForCallerRequestValidator(); + _dto = new GetFeatureFlagForCallerRequest + { + Name = "aname" + }; + } + + [Fact] + public void WhenAllProperties_ThenSucceeds() + { + _validator.ValidateAndThrow(_dto); + } + + [Fact] + public void WhenNameIsEmpty_ThenThrows() + { + _dto.Name = string.Empty; + + _validator.Invoking(x => x.ValidateAndThrow(_dto)) + .Should().Throw() + .WithMessageLike(Resources.GetFeatureFlagForCallerRequestValidator_InvalidName); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Web.Website.UnitTests/Application/FeatureFlagsApplicationSpec.cs b/src/Infrastructure.Web.Website.UnitTests/Application/FeatureFlagsApplicationSpec.cs new file mode 100644 index 00000000..82fa19a6 --- /dev/null +++ b/src/Infrastructure.Web.Website.UnitTests/Application/FeatureFlagsApplicationSpec.cs @@ -0,0 +1,81 @@ +using Application.Interfaces; +using Application.Interfaces.Services; +using Common.FeatureFlags; +using FluentAssertions; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Infrastructure.Web.Interfaces.Clients; +using Moq; +using UnitTesting.Common; +using WebsiteHost.Application; +using Xunit; + +namespace Infrastructure.Web.Website.UnitTests.Application; + +[Trait("Category", "Unit")] +public class FeatureFlagsApplicationSpec +{ + private readonly FeatureFlagsApplication _application; + private readonly Mock _caller; + private readonly Mock _serviceClient; + + public FeatureFlagsApplicationSpec() + { + var hostSettings = new Mock(); + _caller = new Mock(); + _serviceClient = new Mock(); + _application = new FeatureFlagsApplication(_serviceClient.Object, hostSettings.Object); + } + + [Fact] + public async Task WhenGetFeatureFlagForCaller_ThenReturns() + { + _serviceClient.Setup(sc => sc.GetAsync(It.IsAny(), It.IsAny(), + It.IsAny>(), It.IsAny())) + .ReturnsAsync(new GetFeatureFlagResponse + { + Flag = new FeatureFlag + { + Name = "aname", + IsEnabled = true + } + }); + + var result = + await _application.GetFeatureFlagForCallerAsync(_caller.Object, "aname", CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Name.Should().Be("aname"); + result.Value.IsEnabled.Should().BeTrue(); + _serviceClient.Verify(sc => sc.GetAsync(_caller.Object, It.Is(req => + req.Name == "aname" + ), It.IsAny>(), It.IsAny())); + } + + [Fact] + public async Task WhenGetAllFeatureFlags_ThenReturns() + { + _serviceClient.Setup(sc => sc.GetAsync(It.IsAny(), It.IsAny(), + It.IsAny>(), It.IsAny())) + .ReturnsAsync(new GetAllFeatureFlagsResponse + { + Flags = new List + { + new() + { + Name = "aname", + IsEnabled = true + } + } + }); + + var result = + await _application.GetAllFeatureFlagsAsync(_caller.Object, CancellationToken.None); + + result.Should().BeSuccess(); + result.Value.Count.Should().Be(1); + result.Value[0].Name.Should().Be("aname"); + result.Value[0].IsEnabled.Should().BeTrue(); + _serviceClient.Verify(sc => sc.GetAsync(_caller.Object, It.IsAny(), + It.IsAny>(), It.IsAny())); + } +} \ No newline at end of file diff --git a/src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs b/src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs index 96b0d480..dda5b2e7 100644 --- a/src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs +++ b/src/Infrastructure.Worker.Api.IntegrationTests/Stubs/StubServiceClient.cs @@ -9,14 +9,14 @@ public class StubServiceClient : IServiceClient { public Optional LastPostedMessage { get; private set; } = Optional.None; - public Task FireAsync(ICallerContext context, IWebRequestVoid request, + public Task FireAsync(ICallerContext? context, IWebRequestVoid request, Action? requestFilter = null, CancellationToken? cancellationToken = null) { throw new NotImplementedException(); } - public Task FireAsync(ICallerContext context, IWebRequest request, + public Task FireAsync(ICallerContext? context, IWebRequest request, Action? requestFilter, CancellationToken? cancellationToken = null) where TResponse : IWebResponse @@ -24,7 +24,7 @@ public Task FireAsync(ICallerContext context, IWebRequest throw new NotImplementedException(); } - public Task> DeleteAsync(ICallerContext context, + public Task> DeleteAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -32,7 +32,7 @@ public Task FireAsync(ICallerContext context, IWebRequest throw new NotImplementedException(); } - public Task> GetAsync(ICallerContext context, + public Task> GetAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -40,7 +40,7 @@ public Task> GetAsync(ICallerConte throw new NotImplementedException(); } - public Task> PatchAsync(ICallerContext context, + public Task> PatchAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -48,7 +48,7 @@ public Task> PatchAsync(ICallerCon throw new NotImplementedException(); } - public Task> PostAsync(ICallerContext context, + public Task> PostAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() @@ -58,7 +58,7 @@ public Task> PostAsync(ICallerCont return Task.FromResult>(new TResponse()); } - public Task> PutAsync(ICallerContext context, + public Task> PutAsync(ICallerContext? context, IWebRequest request, Action? requestFilter = null, CancellationToken? cancellationToken = null) where TResponse : IWebResponse, new() diff --git a/src/IntegrationTesting.WebApi.Common/ExternalApiSpec.cs b/src/IntegrationTesting.WebApi.Common/ExternalApiSpec.cs new file mode 100644 index 00000000..8d9e2932 --- /dev/null +++ b/src/IntegrationTesting.WebApi.Common/ExternalApiSpec.cs @@ -0,0 +1,120 @@ +using Common.Configuration; +using Common.Extensions; +using Infrastructure.Hosting.Common; +using Infrastructure.Hosting.Common.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace IntegrationTesting.WebApi.Common; + +/// +/// Provides an xUnit collection for running "External" tests together +/// +[CollectionDefinition("External", DisableParallelization = false)] +public class AllExternalSpecs : ICollectionFixture +{ +} + +/// +/// Provides an xUnit class fixture for external integration testing APIs +/// +[UsedImplicitly] +public class ExternalApiSetup : IDisposable +{ + private IHost? _host; + private Action? _overridenTestingDependencies; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_host.Exists()) + { + _host.StopAsync().GetAwaiter().GetResult(); + _host.Dispose(); + } + } + } + + public TService GetRequiredService() + where TService : notnull + { + if (_host.NotExists()) + { + throw new InvalidOperationException("Host has not be started yet!"); + } + + return _host.Services.Resolve(); + } + + public void OverrideTestingDependencies(Action overrideDependencies) + { + _overridenTestingDependencies = overrideDependencies; + } + + public void Start() + { + _host = new HostBuilder() + .ConfigureAppConfiguration(builder => + { + builder + .AddJsonFile("appsettings.Testing.json", true) + .AddJsonFile("appsettings.Testing.local.json", true); + }) + .ConfigureServices((context, services) => + { + services.AddSingleton(new AspNetConfigurationSettings(context.Configuration)); + if (_overridenTestingDependencies.Exists()) + { + _overridenTestingDependencies.Invoke(services); + } + }) + .Build(); + _host.Start(); + } +} + +/// +/// Provides an xUnit class fixture for external integration testing APIs +/// +public abstract class ExternalApiSpec : IClassFixture, IDisposable +{ + protected readonly ExternalApiSetup Setup; + + protected ExternalApiSpec(ExternalApiSetup setup, Action? overrideDependencies = null) + { + if (overrideDependencies.Exists()) + { + setup.OverrideTestingDependencies(overrideDependencies); + } + + setup.Start(); + Setup = setup; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (Setup is IDisposable disposable) + { + disposable.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/IntegrationTesting.WebApi.Common/IntegrationTesting.WebApi.Common.csproj b/src/IntegrationTesting.WebApi.Common/IntegrationTesting.WebApi.Common.csproj index 6ceaaa09..b39a9a3b 100644 --- a/src/IntegrationTesting.WebApi.Common/IntegrationTesting.WebApi.Common.csproj +++ b/src/IntegrationTesting.WebApi.Common/IntegrationTesting.WebApi.Common.csproj @@ -7,6 +7,7 @@ + diff --git a/src/IntegrationTesting.WebApi.Common/Stubs/StubFeatureFlags.cs b/src/IntegrationTesting.WebApi.Common/Stubs/StubFeatureFlags.cs new file mode 100644 index 00000000..b0b63f10 --- /dev/null +++ b/src/IntegrationTesting.WebApi.Common/Stubs/StubFeatureFlags.cs @@ -0,0 +1,49 @@ +using Common; +using Common.FeatureFlags; + +namespace IntegrationTesting.WebApi.Common.Stubs; + +/// +/// Provides a stub for testing +/// +public class StubFeatureFlags : IFeatureFlags +{ + public string? LastGetFlag { get; private set; } + + public Task, Error>> GetAllFlagsAsync(CancellationToken cancellationToken) + { + return Task.FromResult( + new Result, Error>((IReadOnlyList)new List())); + } + + public Task> GetFlagAsync(Flag flag, Optional tenantId, Optional userId, + CancellationToken cancellationToken) + { + LastGetFlag = flag.Name; + return Task.FromResult(new Result(new FeatureFlag + { + Name = flag.Name, + IsEnabled = true + })); + } + + public bool IsEnabled(Flag flag) + { + throw new NotImplementedException(); + } + + public bool IsEnabled(Flag flag, string userId) + { + throw new NotImplementedException(); + } + + public bool IsEnabled(Flag flag, Optional tenantId, string userId) + { + throw new NotImplementedException(); + } + + public void Reset() + { + LastGetFlag = null; + } +} \ No newline at end of file diff --git a/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs b/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs index 723c61c5..7bd5d010 100644 --- a/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs +++ b/src/IntegrationTesting.WebApi.Common/WebApiSpec.cs @@ -6,6 +6,7 @@ using Application.Services.Shared; using Common; using Common.Extensions; +using Common.FeatureFlags; using FluentAssertions; using Infrastructure.Web.Api.Operations.Shared.Identities; using Infrastructure.Web.Api.Operations.Shared.TestingOnly; @@ -87,6 +88,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) .ConfigureTestServices(services => { services.AddSingleton(); + services.AddSingleton(); if (_overridenTestingDependencies.Exists()) { _overridenTestingDependencies.Invoke(services); diff --git a/src/SaaStack.sln b/src/SaaStack.sln index 6df7db04..8e6c2d51 100644 --- a/src/SaaStack.sln +++ b/src/SaaStack.sln @@ -298,6 +298,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Generators.Web.Api.Au EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D3B68FF7-293B-4458-B8D8-49D3DF59B495}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Generators.Common", "Tools.Generators.Common\Tools.Generators.Common.csproj", "{578736A6-7CE1-408D-8217-468F35861F5B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Generators.Common.UnitTests", "Tools.Generators.Common.UnitTests\Tools.Generators.Common.UnitTests.csproj", "{6C654E34-B698-4F23-8757-D50C85F51F5B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Shared.IntegrationTests", "Infrastructure.Shared.IntegrationTests\Infrastructure.Shared.IntegrationTests.csproj", "{A4E40A61-6C36-4C1E-B5D5-68546B2387C3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -929,6 +935,24 @@ Global {E081B52F-A4AC-47A0-B03C-F23BF34CE1E7}.Release|Any CPU.Build.0 = Release|Any CPU {E081B52F-A4AC-47A0-B03C-F23BF34CE1E7}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU {E081B52F-A4AC-47A0-B03C-F23BF34CE1E7}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {578736A6-7CE1-408D-8217-468F35861F5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {578736A6-7CE1-408D-8217-468F35861F5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {578736A6-7CE1-408D-8217-468F35861F5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {578736A6-7CE1-408D-8217-468F35861F5B}.Release|Any CPU.Build.0 = Release|Any CPU + {578736A6-7CE1-408D-8217-468F35861F5B}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {578736A6-7CE1-408D-8217-468F35861F5B}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {6C654E34-B698-4F23-8757-D50C85F51F5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C654E34-B698-4F23-8757-D50C85F51F5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C654E34-B698-4F23-8757-D50C85F51F5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C654E34-B698-4F23-8757-D50C85F51F5B}.Release|Any CPU.Build.0 = Release|Any CPU + {6C654E34-B698-4F23-8757-D50C85F51F5B}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {6C654E34-B698-4F23-8757-D50C85F51F5B}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3}.Release|Any CPU.Build.0 = Release|Any CPU + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3}.ReleaseForDeploy|Any CPU.ActiveCfg = ReleaseForDeploy|Any CPU + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3}.ReleaseForDeploy|Any CPU.Build.0 = ReleaseForDeploy|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5C77A86-38AF-40E4-82FC-617E624B2754} = {508E7DA4-4DF2-4201-955D-CCF70C41AD05} @@ -1072,5 +1096,8 @@ Global {D3B68FF7-293B-4458-B8D8-49D3DF59B495} = {4B1A213C-36A7-41A7-BFC7-B3CFF5795912} {11F60901-1E1C-4B1B-83E8-261269D2681B} = {D3B68FF7-293B-4458-B8D8-49D3DF59B495} {BC14CDD1-E127-4DF7-A1B3-55164CA8D1A4} = {D3B68FF7-293B-4458-B8D8-49D3DF59B495} + {578736A6-7CE1-408D-8217-468F35861F5B} = {BAE0D6F2-6920-4B02-9F30-D71B04B7170D} + {6C654E34-B698-4F23-8757-D50C85F51F5B} = {A25A3BA8-5602-4825-9595-2CF96B166920} + {A4E40A61-6C36-4C1E-B5D5-68546B2387C3} = {9B6B0235-BD3F-4604-8E93-B0112A241C63} EndGlobalSection EndGlobal diff --git a/src/SaaStack.sln.DotSettings b/src/SaaStack.sln.DotSettings index 19cb77d3..faee57f0 100644 --- a/src/SaaStack.sln.DotSettings +++ b/src/SaaStack.sln.DotSettings @@ -822,6 +822,8 @@ public void When$condition$_Then$outcome$() True True True + True + True True True True @@ -908,7 +910,9 @@ public void When$condition$_Then$outcome$() True True True + True True + True True True True @@ -981,6 +985,7 @@ public void When$condition$_Then$outcome$() True True True + True True True True @@ -999,8 +1004,11 @@ public void When$condition$_Then$outcome$() True True True + True True True + True + True True True True @@ -1016,6 +1024,7 @@ public void When$condition$_Then$outcome$() True True True + True True True True diff --git a/src/TestingStubApiHost/Api/StubFlagsmithApi.cs b/src/TestingStubApiHost/Api/StubFlagsmithApi.cs new file mode 100644 index 00000000..4ebf3c22 --- /dev/null +++ b/src/TestingStubApiHost/Api/StubFlagsmithApi.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using Common; +using Common.Configuration; +using Common.FeatureFlags; +using Infrastructure.Web.Api.Interfaces; +using Infrastructure.Web.Api.Operations.Shared._3rdParties.Flagsmith; + +namespace TestingStubApiHost.Api; + +[WebService("/flagsmith")] +public class StubFlagsmithApi : StubApiBase +{ + private static readonly List Flags = GetAllFlags(); + + public StubFlagsmithApi(IRecorder recorder, IConfigurationSettings settings) : base(recorder, settings) + { + } + + public async Task> CreateIdentity( + FlagsmithCreateIdentityRequest request, CancellationToken cancellationToken) + { + await Task.CompletedTask; + Recorder.TraceInformation(null, "StubFlagsmith: CreateIdentity"); + return () => + new PostResult(new FlagsmithCreateIdentityResponse + { + Flags = Flags, + Identifier = request.Identifier, + Traits = request.Traits + }); + } + + public async Task> GetEnvironmentFlags( + FlagsmithGetEnvironmentFlagsRequest request, CancellationToken cancellationToken) + { + await Task.CompletedTask; + Recorder.TraceInformation(null, "StubFlagsmith: GetEnvironmentFlags"); + return () => + new Result(new FlagsmithGetEnvironmentFlagsResponse(Flags)); + } + + private static List GetAllFlags() + { + var allFlags = typeof(Flag).GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(f => f.FieldType == typeof(Flag)) + .Select(f => (Flag)f.GetValue(null)!) + .ToList(); + + var counter = 1000; + return allFlags.Select(f => new FlagsmithFlag + { + Id = null, + Enabled = false, + Value = null, + Feature = new FlagsmithFeature + { + Id = ++counter, + Name = f.Name + } + }).ToList(); + } +} \ No newline at end of file diff --git a/src/TestingStubApiHost/appsettings.json b/src/TestingStubApiHost/appsettings.json index 7f8b5c95..512a07fa 100644 --- a/src/TestingStubApiHost/appsettings.json +++ b/src/TestingStubApiHost/appsettings.json @@ -11,7 +11,7 @@ "ApplicationServices": { "Persistence": { "LocalMachineJsonFileStore": { - "RootPath": "./saastack/stubs" + "RootPath": "./saastack/bananas" } } } diff --git a/src/Tools.Generators.Common.UnitTests/FeatureFlagGeneratorSpec.cs b/src/Tools.Generators.Common.UnitTests/FeatureFlagGeneratorSpec.cs new file mode 100644 index 00000000..1933ed95 --- /dev/null +++ b/src/Tools.Generators.Common.UnitTests/FeatureFlagGeneratorSpec.cs @@ -0,0 +1,97 @@ +using System.Reflection; +using System.Text; +using FluentAssertions; +using JetBrains.Annotations; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Tools.Generators.Common.UnitTests; + +[UsedImplicitly] +public class FeatureFlagGeneratorSpec +{ + private static readonly string[] + AdditionalCompilationAssemblies = + { "System.Runtime.dll", "netstandard.dll" }; //HACK: required to analyze custom attributes + + private static CSharpCompilation CreateCompilation() + { + var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location)!; + + var references = new List + { + MetadataReference.CreateFromFile(typeof(FeatureFlagGenerator).Assembly.Location), + MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) + }; + AdditionalCompilationAssemblies.ToList() + .ForEach(item => references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, item)))); + var compilation = CSharpCompilation.Create("compilation", + new[] + { + CSharpSyntaxTree.ParseText("") + }, + references, + new CSharpCompilationOptions(OutputKind.ConsoleApplication)); + + return compilation; + } + + [Trait("Category", "Unit")] + public class GivenFeatureFlagsResources + { + private GeneratorDriver _driver; + + public GivenFeatureFlagsResources() + { + var generator = new FeatureFlagGenerator(); + var additionalText = new ResourcesFile(); + _driver = CSharpGeneratorDriver.Create(new[] { generator }, new[] { additionalText }); + } + + [Fact] + public void WhenGenerate_ThenGenerates() + { + var result = Generate(CreateCompilation()); + + result.Should().StartWith( + """ + // + using Common.FeatureFlags; + + namespace Common.FeatureFlags; + + /// + partial class Flag + { + public static Flag AFeatureFlag = new Flag("a_feature_flag"); + + } + """); + } + + private string Generate(CSharpCompilation compilation) + { + _driver = _driver.RunGeneratorsAndUpdateCompilation(compilation, out var _, out var _); + return _driver.GetRunResult().Results[0].GeneratedSources[0].SourceText.ToString(); + } + } +} + +public class ResourcesFile : AdditionalText +{ + public override string Path => "FeatureFlags.resx"; + + public override SourceText GetText(CancellationToken cancellationToken = new()) + { + return SourceText.From(""" + + + + a feature flag + + + """, Encoding.UTF8); + } +} \ No newline at end of file diff --git a/src/Tools.Generators.Common.UnitTests/Tools.Generators.Common.UnitTests.csproj b/src/Tools.Generators.Common.UnitTests/Tools.Generators.Common.UnitTests.csproj new file mode 100644 index 00000000..6c3a8c22 --- /dev/null +++ b/src/Tools.Generators.Common.UnitTests/Tools.Generators.Common.UnitTests.csproj @@ -0,0 +1,21 @@ + + + + net7.0 + true + + + + + + + + + + + + + + + + diff --git a/src/Tools.Generators.Common/FeatureFlagGenerator.cs b/src/Tools.Generators.Common/FeatureFlagGenerator.cs new file mode 100644 index 00000000..27dccee4 --- /dev/null +++ b/src/Tools.Generators.Common/FeatureFlagGenerator.cs @@ -0,0 +1,95 @@ +using System.Text; +using System.Xml; +using Common.Extensions; +using Common.FeatureFlags; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Tools.Generators.Common; + +/// +/// A source generator for converting to feature flag values +/// +[Generator] +public class FeatureFlagGenerator : ISourceGenerator +{ + private const string Filename = "FeatureFlags\\Flag.g.cs"; + + public void Initialize(GeneratorInitializationContext context) + { + // No initialization + } + + public void Execute(GeneratorExecutionContext context) + { + var assemblyNamespace = $"{typeof(Flag).Namespace}"; + var classUsingNamespaces = $"using {typeof(Flag).Namespace};"; + + var fileSource = BuildFile(context, assemblyNamespace, classUsingNamespaces, context.CancellationToken); + + context.AddSource(Filename, SourceText.From(fileSource, Encoding.UTF8)); + + return; + + static Dictionary GetFlagResources(GeneratorExecutionContext context, + CancellationToken cancellationToken) + { + var resourceFile = context.AdditionalFiles + .FirstOrDefault(af => af.Path.EndsWith(".resx")); + if (resourceFile is null) + { + return new Dictionary(); + } + + var xml = resourceFile.GetText(cancellationToken)!; + var xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xml.ToString()); + var root = xmlDocument.DocumentElement!; + + var values = root.SelectNodes("/root/data")! + .Cast() + .Select(node => + { + var name = node.Attributes!["name"].Value; + var value = node.SelectSingleNode("value")?.InnerText; + return new KeyValuePair(name, value ?? name); + }); + + return values.ToDictionary(pair => pair.Key, pair => pair.Value); + } + + static string BuildFlagDefinitions(Dictionary flags) + { + var builder = new StringBuilder(); + const string className = nameof(Flag); + foreach (var flag in flags) + { + builder.AppendFormat($" public static {className} {{0}} = new {className}(\"{{1}}\");", flag.Key, + flag.Value.ToSnakeCase()); + builder.AppendLine(); + } + + return builder.ToString(); + } + + static string BuildFile(GeneratorExecutionContext context, string assemblyNamespace, + string allUsingNamespaces, CancellationToken cancellationToken) + { + const string className = nameof(Flag); + var allFlags = GetFlagResources(context, cancellationToken); + var flagDefinitions = BuildFlagDefinitions(allFlags); + + return $@"// +{allUsingNamespaces} + +namespace {assemblyNamespace}; + +/// +partial class {className} +{{ +{flagDefinitions} +}} +"; + } + } +} \ No newline at end of file diff --git a/src/Tools.Generators.Common/README.md b/src/Tools.Generators.Common/README.md new file mode 100644 index 00000000..34ee68db --- /dev/null +++ b/src/Tools.Generators.Common/README.md @@ -0,0 +1,43 @@ +# Source Generator + +This source generator project is only meant to be included by the `Common` project only. + +It's job is to convert all `FeatureFlags` definition (found in the assembly) into instances of the `Flag` class. + +# Development Workarounds + +Source Generators are required to run to build the rest of the codebase. + +Source Generators have to be built in NETSTANDARD2.0 for them to run in Visual Studio, but this is not the case to run in JetBrains Rider. +> This constraint exists to support source generators working in older versions of the .NET Framework, and will exist until Microsoft fix the issue Visual Studio. This is another reason to use JetBrains Rider as the preferred IDE for working with this codebase. + +C# Source Generators have difficulties running in any IDE if the code used in them references code in other projects in the solution, and they also suffer problems if they reference any nuget packages. + +This is especially problematic when those referenced projects have transient dependencies to types in ASP.NET + +If any dependencies are taken, special workarounds (in the project file of this project) are required in order for this source generators to work properly. + +We are avoiding including certain types from any projects in this solution (e.g. from the `Common` project) even though we need it in the code of the Source generator, since that project is dependent on types in AspNet framework. + +To workaround this, we have file-linked certain source files from projects in the solution, so that we can use those symbols in the Source Generator code. + +We have had to hardcode certain other types to avoid referencing AspNet, and these cannot be tracked by tooling if they are changed elsewhere. + +> None of this is ideal. But until we can figure the magic needed to build and run this Source Generator if it uses these types, this may be the best workaround we have for now. + +# Debugging Generators + +You can debug the analyzers easily from the unit tests. + +You can debug your source generator by setting a breakpoint in the code, and then running the `Common-SourceGenerators-Development` run configuration from the `ApiHost1` project with the debugger. (found in the `launchSettings.json` file in any executable project). + + +> Warning: C# source generators are heavily cached. If you try to debug new code that you've added you may need to clear the caches from the old code being used. Otherwise you breakpoints may not hit. + +The most reliable way to reset the generators: + +1. Restart Jetbrains Rider +2. Kill any remaining `.Net Host (dotnet.exe)` processes on your machine, and any remaining `Jetbrains Rider` processes on your machine +3. Restart Rider +4. Set your breakpoints +5. Start debugging the `Common-SourceGenerators-Development` run configuration \ No newline at end of file diff --git a/src/Tools.Generators.Common/Tools.Generators.Common.csproj b/src/Tools.Generators.Common/Tools.Generators.Common.csproj new file mode 100644 index 00000000..d6e5e61e --- /dev/null +++ b/src/Tools.Generators.Common/Tools.Generators.Common.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.0 + $(DefineConstants);GENERATORS_COMMON_PROJECT + latest + enable + true + true + true + + + + + + + + + + <_Parameter1>$(AssemblyName).UnitTests + + + + + + Reference\Common\FeatureFlags\Flag.cs + + + Reference\Common\Extensions\StringExtensions.cs + + + diff --git a/src/Tools.Generators.Web.Api.Authorization/README.md b/src/Tools.Generators.Web.Api.Authorization/README.md index f4ca48f1..1b712671 100644 --- a/src/Tools.Generators.Web.Api.Authorization/README.md +++ b/src/Tools.Generators.Web.Api.Authorization/README.md @@ -29,7 +29,7 @@ We have had to hardcode certain other types to avoid referencing AspNet, and the You can debug the analyzers easily from the unit tests. -You can debug your source generator by setting a breakpoint in the code, and then running the `SourceGenerators-Development-Development` run configuration from the `ApiHost1` project with the debugger. (found in the `launchSettings.json` file in any executable project). +You can debug your source generator by setting a breakpoint in the code, and then running the `Api-SourceGenerators-Development` run configuration from the `ApiHost1` project with the debugger. (found in the `launchSettings.json` file in any executable project). > Warning: C# source generators are heavily cached. If you try to debug new code that you've added you may need to clear the caches from the old code being used. Otherwise you breakpoints may not hit. @@ -40,4 +40,4 @@ The most reliable way to reset the generators: 2. Kill any remaining `.Net Host (dotnet.exe)` processes on your machine, and any remaining `Jetbrains Rider` processes on your machine 3. Restart Rider 4. Set your breakpoints -5. Start debugging the `SourceGenerators-Development-Development` run configuration \ No newline at end of file +5. Start debugging the `Api-SourceGenerators-Development` run configuration \ No newline at end of file diff --git a/src/Tools.Generators.Web.Api.Authorization/Tools.Generators.Web.Api.Authorization.csproj b/src/Tools.Generators.Web.Api.Authorization/Tools.Generators.Web.Api.Authorization.csproj index d74aaccd..4769c66e 100644 --- a/src/Tools.Generators.Web.Api.Authorization/Tools.Generators.Web.Api.Authorization.csproj +++ b/src/Tools.Generators.Web.Api.Authorization/Tools.Generators.Web.Api.Authorization.csproj @@ -2,7 +2,7 @@ netstandard2.0 - GENERATORS_WEB_API_PROJECT + $(DefineConstants);GENERATORS_WEB_API_PROJECT latest enable true diff --git a/src/Tools.Generators.Web.Api.UnitTests/MinimalApiMediatRGeneratorSpec.cs b/src/Tools.Generators.Web.Api.UnitTests/MinimalApiMediatRGeneratorSpec.cs index bccf0e27..601282de 100644 --- a/src/Tools.Generators.Web.Api.UnitTests/MinimalApiMediatRGeneratorSpec.cs +++ b/src/Tools.Generators.Web.Api.UnitTests/MinimalApiMediatRGeneratorSpec.cs @@ -5,7 +5,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; -using Api_MinimalApiMediatRGenerator = Generators::Tools.Generators.Web.Api.MinimalApiMediatRGenerator; +using MinimalApiMediatRGenerator = Generators::Tools.Generators.Web.Api.MinimalApiMediatRGenerator; namespace Tools.Generators.Web.Api.UnitTests; @@ -22,7 +22,7 @@ private static CSharpCompilation CreateCompilation(string sourceCode) var references = new List { - MetadataReference.CreateFromFile(typeof(Api_MinimalApiMediatRGenerator).Assembly.Location), + MetadataReference.CreateFromFile(typeof(MinimalApiMediatRGenerator).Assembly.Location), MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) }; AdditionalCompilationAssemblies.ToList() @@ -45,7 +45,7 @@ public class GivenAServiceClass public GivenAServiceClass() { - var generator = new Api_MinimalApiMediatRGenerator(); + var generator = new MinimalApiMediatRGenerator(); _driver = CSharpGeneratorDriver.Create(generator); } @@ -821,6 +821,90 @@ public class AMethod_ARequest_Handler : global::MediatR.IRequestHandler + { + } + [WebService("aprefix")] + public class AServiceClass : IWebApiService + { + public async Task AMethod(ARequest request, CancellationToken cancellationToken) + { + return ""; + } + } + """); + + var result = Generate(compilation); + + result.Should().Be( + """ + // + using System.Threading; + using System; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Builder; + using Infrastructure.Web.Api.Interfaces; + using Infrastructure.Web.Api.Common.Extensions; + + namespace compilation + { + public static class MinimalApiRegistration + { + public static void RegisterRoutes(this global::Microsoft.AspNetCore.Builder.WebApplication app) + { + var aserviceclassGroup = app.MapGroup("aprefix") + .WithGroupName("AServiceClass") + .RequireCors("__DefaultCorsPolicy") + .AddEndpointFilter() + .AddEndpointFilter() + .AddEndpointFilter(); + #if TESTINGONLY + aserviceclassGroup.MapGet("aroute", + async (global::MediatR.IMediator mediator, [global::Microsoft.AspNetCore.Http.AsParameters] global::ANamespace.ARequest request) => + await mediator.Send(request, global::System.Threading.CancellationToken.None)) + .RequireAuthorization("Token") + .RequireCallerAuthorization("POLICY:{|Features|:{|Platform|:[|basic_features|]},|Roles|:{|Platform|:[|standard|]}}"); + #endif + + } + } + } + + namespace ANamespace.AServiceClassMediatRHandlers + { + #if TESTINGONLY + public class AMethod_ARequest_Handler : global::MediatR.IRequestHandler + { + public async Task Handle(global::ANamespace.ARequest request, global::System.Threading.CancellationToken cancellationToken) + { + var api = new global::ANamespace.AServiceClass(); + var result = await api.AMethod(request, cancellationToken); + return result.HandleApiResult(global::Infrastructure.Web.Api.Interfaces.ServiceOperation.Get); + } + } + #endif + + } + + + """); + } + private string Generate(CSharpCompilation compilation) { _driver = _driver.RunGeneratorsAndUpdateCompilation(compilation, out var _, out var _); diff --git a/src/Tools.Generators.Web.Api.UnitTests/WebApiAssemblyVisitorSpec.cs b/src/Tools.Generators.Web.Api.UnitTests/WebApiAssemblyVisitorSpec.cs index b92cd3e8..96c76d38 100644 --- a/src/Tools.Generators.Web.Api.UnitTests/WebApiAssemblyVisitorSpec.cs +++ b/src/Tools.Generators.Web.Api.UnitTests/WebApiAssemblyVisitorSpec.cs @@ -217,7 +217,7 @@ public GivenAServiceClass() } [Fact] - public void WhenVisitNamedTypeAndNoMethods_ThenCreatesNoRegistrations() + public void WhenVisitNamedTypeAndHasNoMethods_ThenCreatesNoRegistrations() { var type = SetupServiceClass(_compilation); @@ -272,7 +272,7 @@ public void WhenVisitNamedTypeAndVoidReturnType_ThenCreatesNoRegistrations() } [Fact] - public void WhenVisitNamedTypeAndHasNoParameters_ThenCreatesNoRegistrations() + public void WhenVisitNamedTypeAndOperationHasNoParameters_ThenCreatesNoRegistrations() { var taskMetadata = _compilation.GetTypeByMetadataName(typeof(Task<>).FullName!)!; var type = SetupServiceClass(_compilation); @@ -290,7 +290,7 @@ public void WhenVisitNamedTypeAndHasNoParameters_ThenCreatesNoRegistrations() } [Fact] - public void WhenVisitNamedTypeAndHasWrongFirstParameter_ThenCreatesNoRegistrations() + public void WhenVisitNamedTypeAndOperationHasWrongFirstParameter_ThenCreatesNoRegistrations() { var taskMetadata = _compilation.GetTypeByMetadataName(typeof(Task<>).FullName!)!; var type = SetupServiceClass(_compilation); @@ -311,7 +311,7 @@ public void WhenVisitNamedTypeAndHasWrongFirstParameter_ThenCreatesNoRegistratio } [Fact] - public void WhenVisitNamedTypeAndHasWrongSecondParameter_ThenCreatesNoRegistrations() + public void WhenVisitNamedTypeAndOperationHasWrongSecondParameter_ThenCreatesNoRegistrations() { var requestMetadata = _compilation.GetTypeByMetadataName(typeof(IWebRequest).FullName!)!; var stringMetadata = _compilation.GetTypeByMetadataName(typeof(string).FullName!)!; @@ -336,7 +336,7 @@ public void WhenVisitNamedTypeAndHasWrongSecondParameter_ThenCreatesNoRegistrati } [Fact] - public void WhenVisitNamedTypeAndHasNoAttributes_ThenCreatesNoRegistrations() + public void WhenVisitNamedTypeAndRequestDtoHasNoAttributes_ThenCreatesNoRegistrations() { var requestMetadata = _compilation.GetTypeByMetadataName(typeof(IWebRequest).FullName!)!; var cancellationTokenMetadata = _compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName!)!; @@ -361,11 +361,12 @@ public void WhenVisitNamedTypeAndHasNoAttributes_ThenCreatesNoRegistrations() _visitor.OperationRegistrations.Should().BeEmpty(); } + [Trait("Category", "Unit")] public class GivenAServiceOperation { [Fact] - public void WhenVisitNamedTypeAndHasRouteAttribute_ThenCreatesRegistration() + public void WhenVisitNamedTypeAndRequestDtoHasRouteAttribute_ThenCreatesRegistration() { var compilation = CreateCompilation(""" using System; @@ -419,7 +420,7 @@ public string AMethod(ARequest request) } [Fact] - public void WhenVisitNamedTypeAndHasASingleAuthorizeAttribute_ThenCreatesRegistration() + public void WhenVisitNamedTypeAndRequestDtoHasASingleAuthorizeAttribute_ThenCreatesRegistration() { var compilation = CreateCompilation(""" using System; @@ -474,7 +475,7 @@ public string AMethod(ARequest request) } [Fact] - public void WhenVisitNamedTypeAndHasManyAuthorizeAttributes_ThenCreatesRegistration() + public void WhenVisitNamedTypeAndRequestDtoHasManyAuthorizeAttributes_ThenCreatesRegistration() { var compilation = CreateCompilation(""" using System; @@ -530,6 +531,42 @@ public string AMethod(ARequest request) registration.ResponseDtoType.Name.Should().Be("AResponse"); registration.ResponseDtoType.Namespace.Should().Be("ANamespace"); } + + [Fact] + public void WhenVisitNamedTypeAndClassHasRouteAttribute_ThenCreatesRegistration() + { + var compilation = CreateCompilation(""" + using System; + using Infrastructure.Web.Api.Interfaces; + + namespace ANamespace; + + public class AResponse : IWebResponse + { + } + [Infrastructure.Web.Api.Interfaces.RouteAttribute("aroute", ServiceOperation.Get)] + public class ARequest : IWebRequest + { + } + [Infrastructure.Web.Api.Interfaces.WebServiceAttribute("aprefix")] + public class AServiceClass : Infrastructure.Web.Api.Interfaces.IWebApiService + { + public string AMethod(ARequest request) + { + return ""; + } + } + """); + + var serviceClass = compilation.GetTypeByMetadataName("ANamespace.AServiceClass")!; + var visitor = new WebApiAssemblyVisitor(CancellationToken.None, compilation); + + visitor.VisitNamedType(serviceClass); + + visitor.OperationRegistrations.Count.Should().Be(1); + var registration = visitor.OperationRegistrations.First(); + registration.Class.BasePath.Should().Be("aprefix"); + } } private static Mock SetupServiceClass(CSharpCompilation compilation) @@ -547,6 +584,7 @@ private static Mock SetupServiceClass(CSharpCompilation compil .Returns("adisplaystring"); type.Setup(t => t.ContainingNamespace).Returns(@namespace.Object); type.Setup(t => t.Name).Returns("aname"); + type.Setup(t => t.GetAttributes()).Returns(ImmutableArray.Empty); return type; } diff --git a/src/Tools.Generators.Web.Api/MinimalApiMediatRGenerator.cs b/src/Tools.Generators.Web.Api/MinimalApiMediatRGenerator.cs index 5f7dfeef..23465d74 100644 --- a/src/Tools.Generators.Web.Api/MinimalApiMediatRGenerator.cs +++ b/src/Tools.Generators.Web.Api/MinimalApiMediatRGenerator.cs @@ -98,7 +98,11 @@ private static void BuildEndpointRegistrations( { var serviceClassName = serviceRegistrations.Key.Name; var groupName = $"{serviceClassName.ToLowerInvariant()}Group"; - endpointRegistrations.AppendLine($@" var {groupName} = app.MapGroup(string.Empty) + var basePath = serviceRegistrations.FirstOrDefault()?.Class.BasePath; + var prefix = basePath.HasValue() + ? $"\"{basePath}\"" + : "string.Empty"; + endpointRegistrations.AppendLine($@" var {groupName} = app.MapGroup({prefix}) .WithGroupName(""{serviceClassName}"") .RequireCors(""{WebHostingConstants.DefaultCORSPolicyName}"") .AddEndpointFilter() diff --git a/src/Tools.Generators.Web.Api/README.md b/src/Tools.Generators.Web.Api/README.md index 6171466e..917a04c0 100644 --- a/src/Tools.Generators.Web.Api/README.md +++ b/src/Tools.Generators.Web.Api/README.md @@ -29,7 +29,7 @@ We have had to hardcode certain other types to avoid referencing AspNet, and the You can debug the analyzers easily from the unit tests. -You can debug your source generator by setting a breakpoint in the code, and then running the `SourceGenerators-Development-Development` run configuration from the `ApiHost1` project with the debugger. (found in the `launchSettings.json` file in any executable project). +You can debug your source generator by setting a breakpoint in the code, and then running the `Api-SourceGenerators-Development` run configuration from the `ApiHost1` project with the debugger. (found in the `launchSettings.json` file in any executable project). > Warning: C# source generators are heavily cached. If you try to debug new code that you've added you may need to clear the caches from the old code being used. Otherwise you breakpoints may not hit. @@ -40,4 +40,4 @@ The most reliable way to reset the generators: 2. Kill any remaining `.Net Host (dotnet.exe)` processes on your machine, and any remaining `Jetbrains Rider` processes on your machine 3. Restart Rider 4. Set your breakpoints -5. Start debugging the `SourceGenerators-Development-Development` run configuration \ No newline at end of file +5. Start debugging the `Api-SourceGenerators-Development` run configuration \ No newline at end of file diff --git a/src/Tools.Generators.Web.Api/Tools.Generators.Web.Api.csproj b/src/Tools.Generators.Web.Api/Tools.Generators.Web.Api.csproj index 88ed7351..d7d0ac87 100644 --- a/src/Tools.Generators.Web.Api/Tools.Generators.Web.Api.csproj +++ b/src/Tools.Generators.Web.Api/Tools.Generators.Web.Api.csproj @@ -2,7 +2,7 @@ netstandard2.0 - GENERATORS_WEB_API_PROJECT + $(DefineConstants);GENERATORS_WEB_API_PROJECT latest enable true @@ -40,6 +40,9 @@ Reference\Infrastructure.Web.Api.Interfaces\IWebResponse.cs + + Reference\Infrastructure.Web.Api.Interfaces\WebServiceAttribute.cs + Reference\Infrastructure.Web.Api.Interfaces\RouteAttribute.cs diff --git a/src/Tools.Generators.Web.Api/WebApiAssemblyVisitor.cs b/src/Tools.Generators.Web.Api/WebApiAssemblyVisitor.cs index 9280bb0d..da142172 100644 --- a/src/Tools.Generators.Web.Api/WebApiAssemblyVisitor.cs +++ b/src/Tools.Generators.Web.Api/WebApiAssemblyVisitor.cs @@ -35,6 +35,7 @@ public class WebApiAssemblyVisitor : SymbolVisitor private readonly INamedTypeSymbol _voidSymbol; private readonly INamedTypeSymbol _webRequestInterfaceSymbol; private readonly INamedTypeSymbol _webRequestResponseInterfaceSymbol; + private readonly INamedTypeSymbol _webserviceAttributeSymbol; public WebApiAssemblyVisitor(CancellationToken cancellationToken, Compilation compilation) { @@ -42,6 +43,7 @@ public WebApiAssemblyVisitor(CancellationToken cancellationToken, Compilation co _serviceInterfaceSymbol = compilation.GetTypeByMetadataName(typeof(IWebApiService).FullName!)!; _webRequestInterfaceSymbol = compilation.GetTypeByMetadataName(typeof(IWebRequest).FullName!)!; _webRequestResponseInterfaceSymbol = compilation.GetTypeByMetadataName(typeof(IWebRequest<>).FullName!)!; + _webserviceAttributeSymbol = compilation.GetTypeByMetadataName(typeof(WebServiceAttribute).FullName!)!; _routeAttributeSymbol = compilation.GetTypeByMetadataName(typeof(RouteAttribute).FullName!)!; _authorizeAttributeSymbol = compilation.GetTypeByMetadataName(typeof(AuthorizeAttribute).FullName!)!; _authorizeAttributeRolesSymbol = compilation.GetTypeByMetadataName(typeof(Roles).FullName!)!; @@ -159,11 +161,13 @@ private void AddRegistration(INamedTypeSymbol symbol) var usingNamespaces = symbol.GetUsingNamespaces(); var constructors = GetConstructors(); var serviceName = GetServiceName(); + var basePath = GetBasePath(); var classRegistration = new ApiServiceClassRegistration { TypeName = serviceName, Constructors = constructors, - UsingNamespaces = usingNamespaces + UsingNamespaces = usingNamespaces, + BasePath = basePath }; var methods = GetServiceOperationMethods(); @@ -212,6 +216,16 @@ private void AddRegistration(INamedTypeSymbol symbol) return; + string? GetBasePath() + { + if (!HasWebServiceAttribute(symbol, out var attributeData)) + { + return null; + } + + return attributeData!.ConstructorArguments[0].Value!.ToString()!; + } + TypeName GetServiceName() { return new TypeName(symbol.ContainingNamespace.ToDisplayString(), symbol.Name); @@ -400,6 +414,13 @@ bool HasWrongSetOfParameters(IMethodSymbol method) return false; } + // We assume that the class can be decorated with an optional WebServiceAttribute + bool HasWebServiceAttribute(ITypeSymbol classSymbol, out AttributeData? webServiceAttribute) + { + webServiceAttribute = classSymbol.GetAttribute(_webserviceAttributeSymbol); + return webServiceAttribute is not null; + } + // We assume that the request DTO it is decorated with one RouteAttribute bool HasRouteAttribute(IMethodSymbol method, out AttributeData? routeAttribute) { @@ -513,6 +534,8 @@ public override int GetHashCode() public record ApiServiceClassRegistration { + public string? BasePath { get; set; } + public IEnumerable Constructors { get; set; } = new List(); public TypeName TypeName { get; set; } = null!; diff --git a/src/WebsiteHost/Api/FeatureFlags/FeatureFlagsApi.cs b/src/WebsiteHost/Api/FeatureFlags/FeatureFlagsApi.cs new file mode 100644 index 00000000..1634a39e --- /dev/null +++ b/src/WebsiteHost/Api/FeatureFlags/FeatureFlagsApi.cs @@ -0,0 +1,39 @@ +using Common.FeatureFlags; +using Infrastructure.Interfaces; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Interfaces; +using Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; +using WebsiteHost.Application; + +namespace WebsiteHost.Api.FeatureFlags; + +public class FeatureFlagsApi : IWebApiService +{ + private readonly ICallerContextFactory _contextFactory; + private readonly IFeatureFlagsApplication _featureFlagsApplication; + + public FeatureFlagsApi(ICallerContextFactory contextFactory, IFeatureFlagsApplication featureFlagsApplication) + { + _contextFactory = contextFactory; + _featureFlagsApplication = featureFlagsApplication; + } + + public async Task> GetForCaller( + GetFeatureFlagForCallerRequest request, + CancellationToken cancellationToken) + { + var flag = await _featureFlagsApplication.GetFeatureFlagForCallerAsync(_contextFactory.Create(), + request.Name, cancellationToken); + + return () => flag.HandleApplicationResult(f => new GetFeatureFlagResponse { Flag = f }); + } + + public async Task, GetAllFeatureFlagsResponse>> GetAll( + GetAllFeatureFlagsRequest request, + CancellationToken cancellationToken) + { + var flags = await _featureFlagsApplication.GetAllFeatureFlagsAsync(_contextFactory.Create(), cancellationToken); + + return () => flags.HandleApplicationResult(f => new GetAllFeatureFlagsResponse { Flags = f }); + } +} \ No newline at end of file diff --git a/src/WebsiteHost/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs b/src/WebsiteHost/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs new file mode 100644 index 00000000..5fc50380 --- /dev/null +++ b/src/WebsiteHost/Api/FeatureFlags/GetFeatureFlagForCallerRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Infrastructure.Web.Api.Operations.Shared.BackEndForFrontEnd; + +namespace WebsiteHost.Api.FeatureFlags; + +public class GetFeatureFlagForCallerRequestValidator : AbstractValidator +{ + public GetFeatureFlagForCallerRequestValidator() + { + RuleFor(req => req.Name) + .NotEmpty() + .WithMessage(Resources.GetFeatureFlagForCallerRequestValidator_InvalidName); + } +} \ No newline at end of file diff --git a/src/WebsiteHost/Api/Recording/RecordingApi.cs b/src/WebsiteHost/Api/Recording/RecordingApi.cs index eb3f8b40..8c172cac 100644 --- a/src/WebsiteHost/Api/Recording/RecordingApi.cs +++ b/src/WebsiteHost/Api/Recording/RecordingApi.cs @@ -11,12 +11,15 @@ namespace WebsiteHost.Api.Recording; public sealed class RecordingApi : IWebApiService { private readonly ICallerContextFactory _contextFactory; + private readonly IHttpContextAccessor _httpContextAccessor; private readonly IRecordingApplication _recordingApplication; - public RecordingApi(ICallerContextFactory contextFactory, IRecordingApplication recordingApplication) + public RecordingApi(ICallerContextFactory contextFactory, IRecordingApplication recordingApplication, + IHttpContextAccessor httpContextAccessor) { _contextFactory = contextFactory; _recordingApplication = recordingApplication; + _httpContextAccessor = httpContextAccessor; } public async Task RecordCrash(RecordCrashRequest request, @@ -33,8 +36,7 @@ public async Task RecordMeasurement(RecordMeasureRequest request CancellationToken cancellationToken) { var result = await _recordingApplication.RecordMeasurementAsync(_contextFactory.Create(), request.EventName, - request.Additional, - cancellationToken); + request.Additional, _httpContextAccessor.ToClientDetails(), cancellationToken); return () => result.Match(() => new Result(), error => new Result(error)); @@ -44,7 +46,7 @@ public async Task RecordPageView(RecordPageViewRequest request, CancellationToken cancellationToken) { var result = await _recordingApplication.RecordPageViewAsync(_contextFactory.Create(), request.Path, - cancellationToken); + _httpContextAccessor.ToClientDetails(), cancellationToken); return () => result.Match(() => new Result(), error => new Result(error)); @@ -67,10 +69,29 @@ public async Task RecordUsage(RecordUseRequest request, CancellationToken cancellationToken) { var result = await _recordingApplication.RecordUsageAsync(_contextFactory.Create(), request.EventName, - request.Additional, - cancellationToken); + request.Additional, _httpContextAccessor.ToClientDetails(), cancellationToken); return () => result.Match(() => new Result(), error => new Result(error)); } +} + +internal static class HttpContextConversionExtensions +{ + public static ClientDetails ToClientDetails(this IHttpContextAccessor contextAccessor) + { + if (contextAccessor.HttpContext.NotExists()) + { + return new ClientDetails(); + } + + var context = contextAccessor.HttpContext; + + return new ClientDetails + { + IpAddress = context.Connection.RemoteIpAddress?.ToString(), + UserAgent = context.Request.Headers.UserAgent.ToString(), + Referer = context.Request.Headers.Referer.ToString() + }; + } } \ No newline at end of file diff --git a/src/WebsiteHost/Application/FeatureFlagsApplication.cs b/src/WebsiteHost/Application/FeatureFlagsApplication.cs new file mode 100644 index 00000000..228f84d7 --- /dev/null +++ b/src/WebsiteHost/Application/FeatureFlagsApplication.cs @@ -0,0 +1,54 @@ +using Application.Interfaces; +using Application.Interfaces.Services; +using Common; +using Common.FeatureFlags; +using Infrastructure.Web.Api.Common.Extensions; +using Infrastructure.Web.Api.Operations.Shared.Ancillary; +using Infrastructure.Web.Common.Extensions; +using Infrastructure.Web.Interfaces.Clients; + +namespace WebsiteHost.Application; + +public class FeatureFlagsApplication : IFeatureFlagsApplication +{ + private readonly string _hmacSecret; + private readonly IServiceClient _serviceClient; + + public FeatureFlagsApplication(IServiceClient serviceClient, IHostSettings hostSettings) + { + _serviceClient = serviceClient; + _hmacSecret = hostSettings.GetAncillaryApiHostHmacAuthSecret(); + } + + public async Task> GetFeatureFlagForCallerAsync(ICallerContext context, string name, + CancellationToken cancellationToken) + { + var request = new GetFeatureFlagForCallerRequest + { + Name = name, + }; + + var retrieved = await _serviceClient.GetAsync(context, request, null, cancellationToken); + if (!retrieved.IsSuccessful) + { + return retrieved.Error.ToError(); + } + + return retrieved.Value.Flag!; + } + + public async Task, Error>> GetAllFeatureFlagsAsync(ICallerContext context, + CancellationToken cancellationToken) + { + var request = new GetAllFeatureFlagsRequest(); + + var retrieved = await _serviceClient.GetAsync(context, request, req => req.SetHMACAuth(request, _hmacSecret), + cancellationToken); + if (!retrieved.IsSuccessful) + { + return retrieved.Error.ToError(); + } + + return retrieved.Value.Flags; + } +} \ No newline at end of file diff --git a/src/WebsiteHost/Application/IFeatureFlagsApplication.cs b/src/WebsiteHost/Application/IFeatureFlagsApplication.cs new file mode 100644 index 00000000..4977c83e --- /dev/null +++ b/src/WebsiteHost/Application/IFeatureFlagsApplication.cs @@ -0,0 +1,14 @@ +using Application.Interfaces; +using Common; +using Common.FeatureFlags; + +namespace WebsiteHost.Application; + +public interface IFeatureFlagsApplication +{ + Task, Error>> GetAllFeatureFlagsAsync(ICallerContext context, + CancellationToken cancellationToken); + + Task> GetFeatureFlagForCallerAsync(ICallerContext context, string name, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/WebsiteHost/Application/IRecordingApplication.cs b/src/WebsiteHost/Application/IRecordingApplication.cs index 32ac9ce3..44a14a94 100644 --- a/src/WebsiteHost/Application/IRecordingApplication.cs +++ b/src/WebsiteHost/Application/IRecordingApplication.cs @@ -9,13 +9,23 @@ public interface IRecordingApplication Task> RecordCrashAsync(ICallerContext context, string message, CancellationToken cancellationToken); Task> RecordMeasurementAsync(ICallerContext context, string eventName, - Dictionary? additional, CancellationToken cancellationToken); + Dictionary? additional, ClientDetails clientDetails, CancellationToken cancellationToken); - Task> RecordPageViewAsync(ICallerContext context, string path, CancellationToken cancellationToken); + Task> RecordPageViewAsync(ICallerContext context, string path, ClientDetails clientDetails, + CancellationToken cancellationToken); Task> RecordTraceAsync(ICallerContext context, RecorderTraceLevel level, string messageTemplate, List? arguments, CancellationToken cancellationToken); Task> RecordUsageAsync(ICallerContext context, string eventName, - Dictionary? additional, CancellationToken cancellationToken); + Dictionary? additional, ClientDetails clientDetails, CancellationToken cancellationToken); +} + +public class ClientDetails +{ + public string? IpAddress { get; set; } + + public string? Referer { get; set; } + + public string? UserAgent { get; set; } } \ No newline at end of file diff --git a/src/WebsiteHost/Application/RecordingApplication.cs b/src/WebsiteHost/Application/RecordingApplication.cs index b2bd1ab6..8692020e 100644 --- a/src/WebsiteHost/Application/RecordingApplication.cs +++ b/src/WebsiteHost/Application/RecordingApplication.cs @@ -10,13 +10,11 @@ namespace WebsiteHost.Application; public class RecordingApplication : IRecordingApplication { - private readonly IHttpContextAccessor _httpContextAccessor; private readonly IRecorder _recorder; - public RecordingApplication(IRecorder recorder, IHttpContextAccessor httpContextAccessor) + public RecordingApplication(IRecorder recorder) { _recorder = recorder; - _httpContextAccessor = httpContextAccessor; } public Task> RecordCrashAsync(ICallerContext context, string message, @@ -29,10 +27,10 @@ public Task> RecordCrashAsync(ICallerContext context, string messa } public Task> RecordMeasurementAsync(ICallerContext context, string eventName, - Dictionary? additional, + Dictionary? additional, ClientDetails clientDetails, CancellationToken cancellationToken) { - var more = AddClientContext((additional.Exists() + var more = AddClientContext(clientDetails, (additional.Exists() ? additional .Where(pair => pair.Value.Exists()) .ToDictionary(pair => pair.Key, pair => pair.Value) @@ -42,12 +40,12 @@ public Task> RecordMeasurementAsync(ICallerContext context, string return Task.FromResult(Result.Ok); } - public Task> RecordPageViewAsync(ICallerContext context, string path, + public Task> RecordPageViewAsync(ICallerContext context, string path, ClientDetails clientDetails, CancellationToken cancellationToken) { const string eventName = UsageConstants.Events.Web.WebPageVisit; - var additional = AddClientContext(new Dictionary + var additional = AddClientContext(clientDetails, new Dictionary { { UsageConstants.Properties.Path, path } }); @@ -58,10 +56,10 @@ public Task> RecordPageViewAsync(ICallerContext context, string pa } public Task> RecordUsageAsync(ICallerContext context, string eventName, - Dictionary? additional, + Dictionary? additional, ClientDetails clientDetails, CancellationToken cancellationToken) { - var more = AddClientContext((additional.Exists() + var more = AddClientContext(clientDetails, (additional.Exists() ? additional .Where(pair => pair.Value.Exists()) .ToDictionary(pair => pair.Key, pair => pair.Value) @@ -84,37 +82,38 @@ public Task> RecordTraceAsync(ICallerContext context, RecorderTrac case RecorderTraceLevel.Debug: _recorder.TraceDebug(context.ToCall(), messageTemplate, args); return Task.FromResult(Result.Ok); + case RecorderTraceLevel.Information: _recorder.TraceInformation(context.ToCall(), messageTemplate, args); return Task.FromResult(Result.Ok); + case RecorderTraceLevel.Warning: _recorder.TraceWarning(context.ToCall(), messageTemplate, args); return Task.FromResult(Result.Ok); + case RecorderTraceLevel.Error: _recorder.TraceError(context.ToCall(), messageTemplate, args); return Task.FromResult(Result.Ok); + default: _recorder.TraceInformation(context.ToCall(), messageTemplate, args); return Task.FromResult(Result.Ok); } } - private Dictionary AddClientContext(IDictionary additional) + private static Dictionary AddClientContext(ClientDetails clientDetails, + IDictionary additional) { - var ipAddress = _httpContextAccessor.HttpContext!.Connection.RemoteIpAddress?.ToString(); - var userAgent = _httpContextAccessor.HttpContext.Request.Headers.UserAgent.ToString(); - var referredBy = _httpContextAccessor.HttpContext.Request.Headers.Referer.ToString(); - var more = new Dictionary(additional); more.TryAdd(UsageConstants.Properties.Timestamp, DateTime.UtcNow); - more.TryAdd(UsageConstants.Properties.IpAddress, ipAddress.HasValue() - ? ipAddress + more.TryAdd(UsageConstants.Properties.IpAddress, clientDetails.IpAddress.HasValue() + ? clientDetails.IpAddress : "unknown"); - more.TryAdd(UsageConstants.Properties.UserAgent, userAgent.HasValue() - ? userAgent + more.TryAdd(UsageConstants.Properties.UserAgent, clientDetails.UserAgent.HasValue() + ? clientDetails.UserAgent : "unknown"); - more.TryAdd(UsageConstants.Properties.ReferredBy, referredBy.HasValue() - ? referredBy + more.TryAdd(UsageConstants.Properties.ReferredBy, clientDetails.Referer.HasValue() + ? clientDetails.Referer : "unknown"); more.TryAdd(UsageConstants.Properties.Component, UsageConstants.Components.BackEndForFrontEndWebHost); diff --git a/src/WebsiteHost/BackEndForFrontEndModule.cs b/src/WebsiteHost/BackEndForFrontEndModule.cs index 038e1488..7a46835d 100644 --- a/src/WebsiteHost/BackEndForFrontEndModule.cs +++ b/src/WebsiteHost/BackEndForFrontEndModule.cs @@ -51,6 +51,7 @@ public Action RegisterServices return (_, services) => { services.AddControllers(); + services.RegisterUnshared(); services.RegisterUnshared(); services.RegisterUnshared(); services.RegisterUnshared(c => diff --git a/src/WebsiteHost/Resources.Designer.cs b/src/WebsiteHost/Resources.Designer.cs index e0a5ec3f..5fad7217 100644 --- a/src/WebsiteHost/Resources.Designer.cs +++ b/src/WebsiteHost/Resources.Designer.cs @@ -104,6 +104,15 @@ internal static string AuthenticateRequestValidator_InvalidUsername { } } + /// + /// Looks up a localized string similar to The 'Name' is either missing or invalid. + /// + internal static string GetFeatureFlagForCallerRequestValidator_InvalidName { + get { + return ResourceManager.GetString("GetFeatureFlagForCallerRequestValidator_InvalidName", resourceCulture); + } + } + /// /// Looks up a localized string similar to The file '{0}' cannot be found in the directory {rootPath}. Please make sure you have pre-built the JS application by running `npm run build`. /// diff --git a/src/WebsiteHost/Resources.resx b/src/WebsiteHost/Resources.resx index 12b52196..2c785ff4 100644 --- a/src/WebsiteHost/Resources.resx +++ b/src/WebsiteHost/Resources.resx @@ -60,4 +60,7 @@ The file '{0}' cannot be found in the directory {rootPath}. Please make sure you have pre-built the JS application by running `npm run build` + + The 'Name' is either missing or invalid + \ No newline at end of file