├── .config
└── dotnet-tools.json
├── .csharpierrc
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── core_build_publish.yml
│ └── docs.yml
├── .gitignore
├── .readthedocs.yaml
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── CHANGELOG.md
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE
├── LeanCode.CoreLibrary.sln
├── README.md
├── cloud-tests-infrastructure
├── .gitignore
├── .terraform.lock.hcl
├── main.tf
├── outputs.tf
├── postgres.tf
├── readme.md
├── storage.tf
└── variables.tf
├── docs
├── .markdownlint.jsonc
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── README.md
├── cqrs
│ ├── authorization
│ │ └── index.md
│ ├── command
│ │ └── index.md
│ ├── index.md
│ ├── local_execution
│ │ └── index.md
│ ├── operation
│ │ └── index.md
│ ├── pipeline
│ │ ├── adding_custom_middlewares.md
│ │ ├── avoid_commiting_transactions_in_handlers.md
│ │ └── index.md
│ ├── query
│ │ └── index.md
│ └── validation
│ │ └── index.md
├── domain
│ ├── aggregate
│ │ └── index.md
│ ├── domain_event
│ │ └── index.md
│ ├── entity
│ │ └── index.md
│ ├── id
│ │ └── index.md
│ ├── index.md
│ ├── time_provider
│ │ └── index.md
│ └── value_object
│ │ └── index.md
├── external_integrations
│ ├── api_explorer
│ │ └── index.md
│ ├── authorization_ory_kratos
│ │ ├── handling_webhooks.md
│ │ └── index.md
│ ├── emails_sendgrid
│ │ └── index.md
│ ├── feature_flags_configcat
│ │ └── index.md
│ ├── logging_serilog
│ │ ├── index.md
│ │ └── sanitization.md
│ ├── messaging_masstransit
│ │ ├── handling_events.md
│ │ └── index.md
│ ├── observability_open_telemetry
│ │ └── index.md
│ └── push_notifications_fcm
│ │ └── index.md
├── features
│ ├── audit_logs
│ │ └── index.md
│ ├── force_update
│ │ └── index.md
│ └── localization
│ │ └── index.md
├── img
│ ├── leancode_logo_black.png
│ └── leancode_logo_white.png
├── quick_start
│ └── index.md
├── requirements.txt
├── stylesheets
│ └── extra.css
├── tests
│ ├── faking_time
│ │ └── index.md
│ └── integration_tests
│ │ └── index.md
└── v8_migration_guide
│ └── index.md
├── global.json
├── mkdocs.yml
├── src
├── CQRS
│ ├── LeanCode.CQRS.Annotations
│ │ ├── LeanCode.CQRS.Annotations.csproj
│ │ ├── LeanCode.Contracts.pb
│ │ └── PathAliasAttribute.cs
│ ├── LeanCode.CQRS.AspNetCore
│ │ ├── CQRSApplicationBuilder.cs
│ │ ├── CQRSEndpointRouteBuilderExtensions.cs
│ │ ├── CQRSEndpointsDataSource.cs
│ │ ├── CQRSMetrics.cs
│ │ ├── CommandValidatorResolver.cs
│ │ ├── Internals.cs
│ │ ├── LeanCode.CQRS.AspNetCore.csproj
│ │ ├── Local
│ │ │ ├── Context
│ │ │ │ ├── LocalCallContext.cs
│ │ │ │ ├── LocalCallLifetimeFeature.cs
│ │ │ │ ├── LocalCallServiceProvidersFeature.cs
│ │ │ │ ├── LocalHttpRequest.cs
│ │ │ │ ├── NullConnectionInfo.cs
│ │ │ │ ├── NullEndpointFeature.cs
│ │ │ │ ├── NullHeaderDictionary.cs
│ │ │ │ ├── NullHttpResponse.cs
│ │ │ │ ├── NullRequestCookieCollection.cs
│ │ │ │ ├── NullResponseCookies.cs
│ │ │ │ ├── NullSession.cs
│ │ │ │ └── NullWebSocketManager.cs
│ │ │ ├── ILocalCommandExecutor.cs
│ │ │ ├── ILocalOperationExecutor.cs
│ │ │ ├── ILocalQueryExecutor.cs
│ │ │ ├── MiddlewareBasedLocalCommandExecutor.cs
│ │ │ ├── MiddlewareBasedLocalExecutor.cs
│ │ │ ├── MiddlewareBasedLocalOperationExecutor.cs
│ │ │ ├── MiddlewareBasedLocalQueryExecutor.cs
│ │ │ ├── UnauthenticatedCQRSRequestException.cs
│ │ │ ├── UnauthorizedCQRSRequestException.cs
│ │ │ └── UnknownStatusCodeException.cs
│ │ ├── Middleware
│ │ │ ├── CQRSExceptionTranslationMiddleware.cs
│ │ │ ├── CQRSMiddleware.cs
│ │ │ ├── CQRSPipelineFinalizer.cs
│ │ │ ├── CQRSSecurityMiddleware.cs
│ │ │ ├── CQRSValidationMiddleware.cs
│ │ │ ├── NonProductionResponseLoggerMiddleware.cs
│ │ │ └── ResponseLoggerMiddleware.cs
│ │ ├── ObjectExecutorFactory.cs
│ │ ├── Registration
│ │ │ ├── ApiDescriptionTags.cs
│ │ │ ├── CQRSApiDescriptionConfiguration.cs
│ │ │ ├── CQRSApiDescriptionProvider.cs
│ │ │ ├── CQRSObjectsRegistrationSource.cs
│ │ │ ├── EndpointMetadata.cs
│ │ │ ├── ICQRSObjectSource.cs
│ │ │ └── ServiceCollectionRegistrationExtensions.cs
│ │ ├── Serialization
│ │ │ └── ISerializer.cs
│ │ └── ServiceCollectionCQRSExtensions.cs
│ ├── LeanCode.CQRS.Execution
│ │ ├── CQRSObjectMetadata.cs
│ │ ├── CQRSRequestPayload.cs
│ │ ├── CommandExecutionInvalidException.cs
│ │ ├── CommandHandlerNotFoundException.cs
│ │ ├── ExecutionResult.cs
│ │ ├── HttpContextExtensions.cs
│ │ ├── ICommandHandler.cs
│ │ ├── IOperationHandler.cs
│ │ ├── IQueryHandler.cs
│ │ ├── LeanCode.CQRS.Execution.csproj
│ │ ├── OperationHandlerNotFoundException.cs
│ │ └── QueryHandlerNotFoundException.cs
│ ├── LeanCode.CQRS.MassTransitRelay
│ │ ├── AsyncEventsInterceptor.cs
│ │ ├── AsyncEventsInterceptorExtensions.cs
│ │ ├── BusObserverExtensions.cs
│ │ ├── LeanCode.CQRS.MassTransitRelay.csproj
│ │ ├── MassTransitRegistrationConfigurationExtensions.cs
│ │ ├── MassTransitRelayApplicationBuilderExtensions.cs
│ │ ├── MassTransitRelayServiceCollectionExtensions.cs
│ │ ├── Middleware
│ │ │ ├── CommitDatabaseTransactionMiddleware.cs
│ │ │ ├── CorrelationFilter.cs
│ │ │ ├── EventsPublisherFilter.cs
│ │ │ └── EventsPublisherMiddleware.cs
│ │ ├── ProbeSiteExtensions.cs
│ │ ├── ScopedTypedConsumerConsumePipeSpecificationObserver.cs
│ │ └── Testing
│ │ │ ├── AsyncManualResetEvent.cs
│ │ │ ├── ResettableBusActivityMonitor.cs
│ │ │ └── ResettableBusActivityMonitorInitializer.cs
│ ├── LeanCode.CQRS.RemoteHttp.Client
│ │ ├── Exceptions.cs
│ │ ├── HttpCommandsExecutor.cs
│ │ ├── HttpOperationsExecutor.cs
│ │ ├── HttpQueriesExecutor.cs
│ │ ├── HttpResponseMessageExtensions.cs
│ │ └── LeanCode.CQRS.RemoteHttp.Client.csproj
│ ├── LeanCode.CQRS.ScopedTransaction
│ │ ├── LeanCode.CQRS.ScopedTransaction.csproj
│ │ ├── PessimisticConcurrencyAttribute.cs
│ │ └── ScopedTransactionMiddleware.cs
│ ├── LeanCode.CQRS.Security
│ │ ├── ClaimsPrincipalExtensions.cs
│ │ ├── CustomAuthorizer.cs
│ │ ├── CustomAuthorizerNotFoundException.cs
│ │ ├── DefaultPermissionAuthorizer.cs
│ │ ├── Exceptions
│ │ │ ├── InsufficientPermissionException.cs
│ │ │ └── UnauthenticatedException.cs
│ │ ├── IAuthorizerResolver.cs
│ │ ├── IRoleRegistration.cs
│ │ ├── LeanCode.CQRS.Security.csproj
│ │ ├── Role.cs
│ │ └── RoleRegistry.cs
│ ├── LeanCode.CQRS.Validation.Fluent
│ │ ├── AdapterLoader.cs
│ │ ├── FluentValidationCommandValidatorAdapter.cs
│ │ ├── FluentValidationErrorState.cs
│ │ ├── FluentValidationExtensions.cs
│ │ ├── LeanCode.CQRS.Validation.Fluent.csproj
│ │ ├── ServiceProviderExtensions.cs
│ │ └── ValidationContextExtensions.cs
│ └── LeanCode.CQRS.Validation
│ │ ├── ICommandValidator.cs
│ │ ├── ICommandValidatorResolver.cs
│ │ └── LeanCode.CQRS.Validation.csproj
├── Core
│ ├── LeanCode.Components.Autofac
│ │ ├── IAppModule.cs
│ │ └── LeanCode.Components.Autofac.csproj
│ ├── LeanCode.Components
│ │ ├── LeanCode.Components.csproj
│ │ ├── ServiceCollectionExtensions.cs
│ │ └── TypesCatalog.cs
│ ├── LeanCode.Startup.Autofac
│ │ ├── LeanCode.Startup.Autofac.csproj
│ │ ├── LeanProgram.cs
│ │ └── LeanStartup.cs
│ └── LeanCode.Startup.MicrosoftDI
│ │ ├── LeanCode.Startup.MicrosoftDI.csproj
│ │ ├── LeanProgram.cs
│ │ └── LeanStartup.cs
├── Directory.Build.props
├── Domain
│ ├── LeanCode.DomainModels.EF
│ │ ├── CachingEFRepository.cs
│ │ ├── CollectionExtensions.cs
│ │ ├── DateTimeOnlyConverter.cs
│ │ ├── DbContextExtensions.cs
│ │ ├── EFRepository.cs
│ │ ├── IdConverter.cs
│ │ ├── LeanCode.DomainModels.EF.csproj
│ │ ├── ModelBuilderExtensions.cs
│ │ ├── PropertiesConfigurationBuilderExtensions.cs
│ │ ├── PropertyBuilderExtensions.cs
│ │ ├── QueryExpressionVisitorInterceptor.cs
│ │ ├── SimpleEFRepository.cs
│ │ ├── TimestampTzExpressionInterceptor.cs
│ │ └── TypedIdConverter.cs
│ ├── LeanCode.DomainModels.Generators
│ │ ├── AnalyzerReleases.Shipped.md
│ │ ├── IdSource.cs
│ │ ├── LeanCode.DomainModels.Generators.csproj
│ │ ├── TypedIdData.cs
│ │ ├── TypedIdFormat.cs
│ │ └── TypedIdGenerator.cs
│ ├── LeanCode.DomainModels
│ │ ├── DataAccess
│ │ │ ├── IRepository.cs
│ │ │ └── IRepositoryExtensions.cs
│ │ ├── Ids
│ │ │ ├── ITypedId.cs
│ │ │ ├── TypedIdAttribute.cs
│ │ │ └── TypedIdConverter.cs
│ │ ├── LeanCode.DomainModels.csproj
│ │ ├── Model
│ │ │ ├── DomainEvents.cs
│ │ │ ├── IAggregateRoot.cs
│ │ │ ├── IDomainEvent.cs
│ │ │ ├── IDomainEventInterceptor.cs
│ │ │ ├── IEntity.cs
│ │ │ ├── IOptimisticConcurrency.cs
│ │ │ ├── ISoftDeletable.cs
│ │ │ ├── Id.cs
│ │ │ ├── SId.cs
│ │ │ ├── TimestampTz.cs
│ │ │ ├── TypedIdConverterAttribute.cs
│ │ │ └── ValueObject.cs
│ │ └── Ulids
│ │ │ ├── Ulid.cs
│ │ │ ├── UlidJsonConverter.cs
│ │ │ └── UlidRandomProvider.cs
│ └── LeanCode.TimeProvider
│ │ ├── AsyncLocalTimeProvider.cs
│ │ ├── LeanCode.TimeProvider.csproj
│ │ ├── Time.cs
│ │ └── UtcSystemTimeProvider.cs
├── Helpers
│ ├── LeanCode.UrlHelper
│ │ ├── LeanCode.UrlHelper.csproj
│ │ └── UrlHelper.cs
│ └── LeanCode.UserIdExtractors
│ │ ├── Extractors
│ │ ├── GuidUserIdExtractor.cs
│ │ ├── PrefixedTypedUserIdExtractor.cs
│ │ ├── RawTypedUserIdExtractor.cs
│ │ └── StringUserIdExtractor.cs
│ │ ├── IUserIdExtractor.cs
│ │ ├── LeanCode.UserIdExtractors.csproj
│ │ └── ServiceProviderExtensions.cs
├── Infrastructure
│ ├── LeanCode.AI.Contracts
│ │ ├── LeanCode.AI.Contracts.csproj
│ │ ├── McpToolAttribute.cs
│ │ └── McpToolHints.cs
│ ├── LeanCode.AI.McpServer
│ │ ├── CQRSAIFunction.cs
│ │ ├── CQRSObjectEnumerator.cs
│ │ ├── LeanCode.AI.McpServer.csproj
│ │ └── McpServerBuilderExtensions.cs
│ ├── LeanCode.AuditLogs
│ │ ├── AppicationBuilderExtensions.cs
│ │ ├── AuditLogsConsumer.cs
│ │ ├── AuditLogsExtensions.cs
│ │ ├── AuditLogsFilter.cs
│ │ ├── AuditLogsMiddleware.cs
│ │ ├── AuditLogsPublisher.cs
│ │ ├── AzureBlobAuditLogStorage.cs
│ │ ├── ChangedEntitiesExtractor.cs
│ │ ├── EntityData.cs
│ │ ├── IAuditLogStorage.cs
│ │ ├── LeanCode.AuditLogs.csproj
│ │ └── MassTransintRegistrationConfigurationExtensions.cs
│ ├── LeanCode.AzureIdentity
│ │ ├── AzureCredentialConfiguration.cs
│ │ ├── DefaultLeanCodeCredential.cs
│ │ ├── IHostBuilderExtensions.cs
│ │ └── LeanCode.AzureIdentity.csproj
│ ├── LeanCode.ClientCredentialsHandler
│ │ ├── ClientCredentialsConfiguration.cs
│ │ ├── ClientCredentialsHandler.cs
│ │ └── LeanCode.ClientCredentialsHandler.csproj
│ ├── LeanCode.ConfigCat
│ │ ├── ConfigCatExtensions.cs
│ │ ├── ConfigCatInitializer.cs
│ │ ├── ConfigCatOptions.cs
│ │ ├── ConfigCatToMSLoggerAdapter.cs
│ │ └── LeanCode.ConfigCat.csproj
│ ├── LeanCode.Dapper
│ │ ├── DbContextExtensions.cs
│ │ ├── LeanCode.Dapper.csproj
│ │ ├── RawQueryValidator.cs
│ │ └── RawSqlQueryAttribute.cs
│ ├── LeanCode.EFMigrator
│ │ ├── BaseFactory.cs
│ │ ├── LeanCode.EFMigrator.csproj
│ │ ├── MigrationsConfig.cs
│ │ └── Migrator.cs
│ ├── LeanCode.Firebase.FCM
│ │ ├── FCMClient.cs
│ │ ├── FCMSendException.cs
│ │ ├── FCMServiceCollectionExtensions.cs
│ │ ├── IPushNotificationTokenStore.cs
│ │ ├── LeanCode.Firebase.FCM.csproj
│ │ ├── LocalizedNotification.cs
│ │ ├── ModelBuilderExtensions.cs
│ │ ├── NotificationDataConverter.cs
│ │ ├── Notifications.cs
│ │ ├── PushNotificationTokenEntity.cs
│ │ └── PushNotificationTokenStore.cs
│ ├── LeanCode.Firebase.Firestore
│ │ ├── FirestoreDatabase.cs
│ │ ├── FirestoreModule.cs
│ │ └── LeanCode.Firebase.Firestore.csproj
│ ├── LeanCode.Firebase
│ │ ├── FirebaseConfiguration.cs
│ │ └── LeanCode.Firebase.csproj
│ ├── LeanCode.ForceUpdate.Contracts
│ │ ├── LeanCode.Contracts.pb
│ │ ├── LeanCode.ForceUpdate.Contracts.csproj
│ │ └── VersionSupport.cs
│ ├── LeanCode.ForceUpdate
│ │ ├── CQRSServicesBuilderCQRSExtensions.cs
│ │ ├── LeanCode.ForceUpdate.Services
│ │ │ ├── CQRS
│ │ │ │ └── VersionSupportQH.cs
│ │ │ └── VersionHandler.cs
│ │ ├── LeanCode.ForceUpdate.csproj
│ │ └── VersionsConfiguration.cs
│ ├── LeanCode.Kratos
│ │ ├── KratosAuthenticationHandler.cs
│ │ ├── KratosAuthenticationOptions.cs
│ │ ├── KratosDefaults.cs
│ │ ├── KratosExtensions.cs
│ │ ├── KratosWebHookHandlerBase.cs
│ │ ├── LeanCode.Kratos.csproj
│ │ └── Model
│ │ │ ├── Identity.cs
│ │ │ └── IdentityAddress.cs
│ ├── LeanCode.Localization
│ │ ├── LeanCode.Localization.csproj
│ │ ├── LocalizationConfiguration.cs
│ │ ├── LocalizationServiceProviderExtensions.cs
│ │ ├── LocalizedResourceNotFoundException.cs
│ │ └── StringLocalizers
│ │ │ ├── IStringLocalizer.cs
│ │ │ ├── IStringLocalizerExtensions.cs
│ │ │ └── ResourceManagerStringLocalizer.cs
│ ├── LeanCode.Logging.AspNetCore
│ │ ├── IHostBuilderExtensions.cs
│ │ ├── LeanCode.Logging.AspNetCore.csproj
│ │ ├── LeanCodeLoggingBuilderExtensions.cs
│ │ ├── SerilogExtensions.cs
│ │ └── UserIdLogsCorrelationMiddleware.cs
│ ├── LeanCode.Logging
│ │ ├── BaseSanitizer.cs
│ │ ├── ContextualLogger.cs
│ │ ├── ILogger.cs
│ │ ├── LeanCode.Logging.csproj
│ │ ├── LeanCodeLoggingBuilderExtensions.cs
│ │ └── NullLogger.cs
│ ├── LeanCode.Mixpanel
│ │ ├── IMixpanelEvent.cs
│ │ ├── LeanCode.Mixpanel.csproj
│ │ ├── MixpanelAnalytics.cs
│ │ ├── MixpanelConfiguration.cs
│ │ └── MixpanelServiceCollectionExtensions.cs
│ ├── LeanCode.Npgsql.ActiveDirectory
│ │ ├── LeanCode.Npgsql.ActiveDirectory.csproj
│ │ └── NpgsqlDataSourceBuilderExtensions.cs
│ ├── LeanCode.OpenTelemetry
│ │ ├── ActivityLogsEnricher.cs
│ │ ├── Datadog
│ │ │ └── DatadogIdConverter.cs
│ │ ├── IdentityTraceAttributesFromBaggageProcessor.cs
│ │ ├── IdentityTraceAttributesMiddleware.cs
│ │ ├── IdentityTraceBaggageHelpers.cs
│ │ ├── LeanCode.OpenTelemetry.csproj
│ │ ├── LeanCodeActivitySource.cs
│ │ ├── LeanCodeMetrics.cs
│ │ ├── MeterBuilderExtensions.cs
│ │ └── TracerBuilderExtensions.cs
│ ├── LeanCode.PeriodicService
│ │ ├── IPeriodicAction.cs
│ │ ├── LeanCode.PeriodicService.csproj
│ │ ├── PeriodicHostedService.cs
│ │ └── ServiceCollectionExtensions.cs
│ ├── LeanCode.SendGrid
│ │ ├── EnumerableAsyncExtensions.cs
│ │ ├── LeanCode.SendGrid.csproj
│ │ ├── SendGridException.cs
│ │ ├── SendGridLocalizedRazorMessage.cs
│ │ ├── SendGridRazorClient.cs
│ │ ├── SendGridRazorMessage.cs
│ │ ├── SendGridRazorMessageExtensions.cs
│ │ ├── SendGridResponse.cs
│ │ └── SendGridServiceCollectionExtensions.cs
│ ├── LeanCode.Serialization
│ │ ├── JsonConverters
│ │ │ ├── JsonLaxDateOnlyConverter.cs
│ │ │ ├── JsonLaxDateTimeOffsetConverter.cs
│ │ │ ├── JsonLaxTimeOnlyConverter.cs
│ │ │ └── JsonStringEnumConverters.cs
│ │ └── LeanCode.Serialization.csproj
│ ├── LeanCode.SmsSender
│ │ ├── Exceptions
│ │ │ └── Exceptions.cs
│ │ ├── ISmsSender.cs
│ │ ├── LeanCode.SmsSender.csproj
│ │ ├── SmsApiClient.cs
│ │ ├── SmsApiConfiguration.cs
│ │ └── SmsSenderServiceCollectionExtensions.cs
│ ├── LeanCode.ViewRenderer.Razor
│ │ ├── CompiledViewsCache.cs
│ │ ├── Extensions
│ │ │ ├── Layout.cs
│ │ │ ├── LayoutDirectivePass.cs
│ │ │ └── LayoutNode.cs
│ │ ├── Internals.cs
│ │ ├── LeanCode.ViewRenderer.Razor.csproj
│ │ ├── RazorViewRenderer.cs
│ │ ├── RazorViewRendererOptions.cs
│ │ ├── RazorViewRendererServiceCollectionExtensions.cs
│ │ ├── ViewBase
│ │ │ ├── AttributeValue.cs
│ │ │ ├── BaseView.cs
│ │ │ └── HelperResult.cs
│ │ ├── ViewCompiler.cs
│ │ └── ViewLocator.cs
│ └── LeanCode.ViewRenderer
│ │ ├── CompilationFailedException.cs
│ │ ├── IViewRenderer.cs
│ │ ├── LeanCode.ViewRenderer.csproj
│ │ └── ViewNotFoundException.cs
├── Testing
│ ├── LeanCode.DomainModels.EventsExecution.TestHelpers
│ │ ├── EventsInterceptor.cs
│ │ └── LeanCode.DomainModels.EventsExecution.TestHelpers.csproj
│ ├── LeanCode.IntegrationTestHelpers
│ │ ├── ConfigurationOverrides.cs
│ │ ├── DbContextInitializer.cs
│ │ ├── HttpClientExtensions.cs
│ │ ├── LeanCode.IntegrationTestHelpers.csproj
│ │ ├── LeanCodeTestFactory.cs
│ │ ├── TestAuthenticationHandler.cs
│ │ └── TestConnectionString.cs
│ └── LeanCode.TimeProvider.TestHelpers
│ │ ├── LeanCode.TimeProvider.TestHelpers.csproj
│ │ └── TestTimeProvider.cs
└── Tools
│ └── LeanCode.CodeAnalysis
│ ├── AnalyzerReleases.Shipped.md
│ ├── Analyzers
│ ├── CancellationTokensShouldFollowNamingConvention.cs
│ ├── EnsureCQRSHandlersAreInProperNamespace.cs
│ ├── EnsureCQRSHandlersFollowNamingConvention.cs
│ ├── EnsureCommandValidatorsFollowNamingConvention.cs
│ ├── EnsureCommandsQueriesAndOperationsHaveAuthorizers.cs
│ ├── NamedTypeSymbolExtensions.cs
│ └── SuggestCommandsHaveValidators.cs
│ ├── CodeActions
│ ├── AddAuthorizationAttributeCodeAction.cs
│ ├── AddCommandValidatorCodeAction.cs
│ ├── FixCQRSHandlerNamespaceCodeAction.cs
│ ├── FixCQRSHandlerNamingCodeAction.cs
│ ├── FixCancellationTokenNamingCodeAction.cs
│ ├── FixCommandValidatorNamingCodeAction.cs
│ └── Helpers.cs
│ ├── CodeFixProviders
│ ├── AddAuthorizationAttributeCodeFixProvider.cs
│ ├── AddCommandValidatorCodeFixProvider.cs
│ ├── FixCQRSHandlerNamespaceCodeFixProvider.cs
│ ├── FixCQRSHandlerNamingCodeFixProvider.cs
│ ├── FixCancellationTokenNamingCodeFixProvider.cs
│ └── FixCommandValidatorNamingCodeFixProvider.cs
│ ├── DiagnosticsIds.cs
│ ├── LeanCode.CodeAnalysis.csproj
│ └── local-debug.txt
├── test-bed
├── Api
│ ├── TestCommand.cs
│ └── TestQuery.cs
├── LeanCode.TestBed.csproj
└── Program.cs
└── test
├── CQRS
├── LeanCode.CQRS.AspNetCore.Tests.Integration
│ ├── CustomAuthorizer.cs
│ ├── HttpContextCustomAuthorizer.cs
│ ├── LeanCode.CQRS.AspNetCore.Tests.Integration.csproj
│ ├── RemoteCQRSCommandsTests.cs
│ ├── RemoteCQRSOperationsTests.cs
│ ├── RemoteCQRSQueriesTests.cs
│ ├── RemoteCQRSTestsBase.cs
│ ├── TestCommand.cs
│ ├── TestOperation.cs
│ └── TestQuery.cs
├── LeanCode.CQRS.AspNetCore.Tests
│ ├── CQRSEndpointsDataSourceTests.cs
│ ├── CQRSObjectsRegistrationSourceTests.cs
│ ├── CQRSServicesBuilderTests.cs
│ ├── CommandValidatorResolverTests.cs
│ ├── LeanCode.CQRS.AspNetCore.Tests.csproj
│ ├── Local
│ │ ├── Context
│ │ │ ├── LocalCallContextTests.cs
│ │ │ ├── LocalCallLifetimeFeatureTests.cs
│ │ │ ├── LocalCallServiceProvidersFeatureTests.cs
│ │ │ ├── LocalHttpRequestTests.cs
│ │ │ ├── NullConnectionInfoTests.cs
│ │ │ ├── NullEndpointFeatureTests.cs
│ │ │ ├── NullHeaderDictionaryTests.cs
│ │ │ ├── NullHttpResponseTests.cs
│ │ │ ├── NullRequestCookieCollectionTests.cs
│ │ │ ├── NullResponseCookiesTests.cs
│ │ │ ├── NullSessionTests.cs
│ │ │ └── NullWebSocketManagerTests.cs
│ │ ├── MiddlewareBasedExecutorIntegrationTests.cs
│ │ ├── MiddlewareBasedLocalExecutorTests.cs
│ │ └── MiddlewaresForLocalExecutionTests.cs
│ ├── Middleware
│ │ ├── CQRSExceptionTranslationMiddlewareTests.cs
│ │ ├── CQRSMiddlewareTestBase.cs
│ │ ├── CQRSMiddlewareTests.cs
│ │ ├── CQRSSecurityMiddlewareTests.cs
│ │ ├── CQRSValidationMiddlewareTests.cs
│ │ └── TestHelpers.cs
│ ├── ObjectExecutorFactoryTests.cs
│ ├── Registration
│ │ ├── ApiDescriptionTagsTests.cs
│ │ └── CQRSApiDescriptionProviderTests.cs
│ ├── ServiceCollectionExtensionsTests.cs
│ └── ServiceProviderRegistrationExtensionsTests.cs
├── LeanCode.CQRS.MassTransitRelay.Tests
│ ├── Integration
│ │ ├── HandledLog.cs
│ │ ├── MassTransitIntegrationTest.cs
│ │ ├── TestApp.cs
│ │ ├── TestCommand.cs
│ │ ├── TestConsumers.cs
│ │ ├── TestDbContext.cs
│ │ └── TestEvents.cs
│ ├── LeanCode.CQRS.MassTransitRelay.Tests.csproj
│ ├── MassTransitRegistrationConfigurationExtensionsTests.cs
│ ├── Middleware
│ │ └── EventsPublisherFilterTests.cs
│ ├── ScopedFiltersTests.cs
│ └── Testing
│ │ └── ResettableBusActivityMonitorTests.cs
├── LeanCode.CQRS.RemoteHttp.Client.Tests
│ ├── HttpCommandsExecutorTests.cs
│ ├── HttpOperationsExecutorTests.cs
│ ├── HttpQueriesExecutorTests.cs
│ ├── LeanCode.CQRS.RemoteHttp.Client.Tests.csproj
│ └── ShortcircuitingJsonHandler.cs
├── LeanCode.CQRS.Security.Tests
│ ├── ClaimsPrinicipalTests.cs
│ ├── CustomAuthorizerTests.cs
│ └── LeanCode.CQRS.Security.Tests.csproj
└── LeanCode.CQRS.Validation.Fluent.Tests
│ ├── AdapterWithAsyncValidatorIntegrationTests.cs
│ ├── LeanCode.CQRS.Validation.Fluent.Tests.csproj
│ └── ModuleTests.cs
├── Core
└── LeanCode.Components.Startup.Tests
│ ├── LeanCode.Components.Startup.Tests.csproj
│ └── LeanStartupTests.cs
├── Directory.Build.props
├── Domain
├── LeanCode.DomainModels.EF.Tests
│ ├── CollectionExtensionsTests.cs
│ ├── DateTimeOnlyConverterTests.cs
│ ├── EFRepositoryTrackingCompositeTests.cs
│ ├── EFRepositoryTrackingTests.cs
│ ├── LeanCode.DomainModels.EF.Tests.csproj
│ ├── ModelBuilderExtensionsTests.OptimisticConcurrency.cs
│ ├── ModelConfigurationBuilderWrapper.cs
│ ├── TimestampTzExpressionInterceptorTests.cs
│ ├── TypedIdConverterTests.cs
│ ├── TypedIdDatabaseIntegrationTests.cs
│ └── TypedIds.cs
├── LeanCode.DomainModels.EventsExecution.TestHelpers.Tests
│ ├── Events.cs
│ ├── EventsInterceptorTests.RaceConds.cs
│ ├── EventsInterceptorTests.cs
│ └── LeanCode.DomainModels.EventsExecution.TestHelpers.Tests.csproj
├── LeanCode.DomainModels.Tests
│ ├── DataAccess
│ │ └── IRepositoryExtensionsTests.cs
│ ├── Ids
│ │ ├── GeneratorRunner.cs
│ │ ├── GuidTests.cs
│ │ ├── IntTests.cs
│ │ ├── InvalidConstructTests.cs
│ │ ├── LongTests.cs
│ │ ├── PrefixedGuidTests.cs
│ │ ├── PrefixedGuidVariationsTests.cs
│ │ ├── PrefixedUlidTests.cs
│ │ ├── PrefixedUlidVariationsTests.cs
│ │ └── ValidConstructTests.cs
│ ├── LeanCode.DomainModels.Tests.csproj
│ ├── Model
│ │ ├── SIdTests.cs
│ │ ├── TimestampTzTests.cs
│ │ ├── TypedIdSerializationTests.cs
│ │ ├── TypedIdTests.cs
│ │ └── ValueObjectTests.cs
│ └── Ulids
│ │ ├── UlidJsonConverterTests.cs
│ │ └── UlidTests.cs
└── LeanCode.TimeProvider.Tests
│ ├── LeanCode.TimeProvider.Tests.csproj
│ └── TimeProviderTests.cs
├── Helpers
└── LeanCode.UserIdExtractors.Tests
│ ├── LeanCode.UserIdExtractors.Tests.csproj
│ ├── UserIdExtractorsRegistrationTests.cs
│ └── UserIdExtractorsTests.cs
├── Infrastructure
├── LeanCode.AuditLogs.Tests
│ ├── AuditLogsFilterTests.cs
│ ├── AuditLogsIntegrationTests.cs
│ ├── AuditLogsMiddlewareTests.cs
│ ├── AuditLogsPublisherTests.cs
│ ├── ChangedEntititesExtractorTests.cs
│ ├── LeanCode.AuditLogs.Tests.csproj
│ └── TestDbContext.cs
├── LeanCode.Azure.Tests
│ ├── AzureStorageAuditLogIntegrationTests.cs
│ ├── Env.cs
│ ├── LeanCode.Azure.Tests.csproj
│ └── PostgresAD
│ │ └── NpgsqlActiveDirectoryAuthenticationTests.cs
├── LeanCode.AzureIdentity.Tests
│ ├── AzureIdentityFact.cs
│ ├── DefaultLeanCodeCredentialsTests.Authorization.cs
│ ├── DefaultLeanCodeCredentialsTests.Configuration.cs
│ ├── LeanCode.AzureIdentity.Tests.csproj
│ └── Properties.cs
├── LeanCode.Dapper.Tests
│ ├── DbContextExtensionsTests.cs
│ └── LeanCode.Dapper.Tests.csproj
├── LeanCode.Firebase.FCM.Tests
│ ├── FCMClientLocalizationTests.cs
│ ├── FCMClientTests.cs
│ ├── FCMFact.cs
│ ├── LeanCode.Firebase.FCM.Tests.csproj
│ ├── NotificationConversionTests.cs
│ └── StubStore.cs
├── LeanCode.ForceUpdate.Tests
│ ├── ForceUpdateTests.cs
│ └── LeanCode.ForceUpdate.Tests.csproj
├── LeanCode.Kratos.Tests
│ ├── KratosAuthenticationHandlerTests.cs
│ ├── KratosWebHookHandlerTests.cs
│ └── LeanCode.Kratos.Tests.csproj
├── LeanCode.Localization.Tests
│ ├── Infrastructure
│ │ └── LeanCode.Localization.Tests
│ │ │ └── ResourceManagerStringLocalizerTests.cs
│ ├── LeanCode.Localization.Tests.csproj
│ ├── ResourceManagerStringLocalizerTests.pl.resx
│ └── ResourceManagerStringLocalizerTests.resx
├── LeanCode.Logging.AspNetCore.Tests
│ ├── LeanCode.Logging.AspNetCore.Tests.csproj
│ └── LoggerInjectionTests.cs
├── LeanCode.Logging.Tests
│ ├── BaseSanitizerTests.cs
│ └── LeanCode.Logging.Tests.csproj
├── LeanCode.Mixpanel.Tests
│ ├── LeanCode.Mixpanel.Tests.csproj
│ └── MixpanelAnalyticsTests.cs
├── LeanCode.SendGrid.Tests
│ ├── LeanCode.SendGrid.Tests.csproj
│ └── SendGridRazorClientTests.cs
├── LeanCode.Serialization.Tests
│ ├── JsonContentTests.cs
│ ├── JsonLaxConvertersTests
│ │ ├── JsonLaxDateOnlyConverterTests.cs
│ │ ├── JsonLaxDateTimeOffsetConverterTests.cs
│ │ └── JsonLaxTimeOnlyConverterTests.cs
│ ├── JsonStringEnumConvertersTests.cs
│ └── LeanCode.Serialization.Tests.csproj
├── LeanCode.SmsSender.Tests
│ ├── LeanCode.SmsSender.Tests.csproj
│ └── SmsSenderClientTests.cs
└── LeanCode.ViewRenderer.Razor.Tests
│ ├── CompiledViewsCacheTests.cs
│ ├── LeanCode.ViewRenderer.Razor.Tests.csproj
│ ├── RazorViewRendererTests.cs
│ ├── ViewCompilerTests.cs
│ ├── ViewLocatorTests.cs
│ └── Views
│ ├── A
│ ├── ViewA.cshtml
│ ├── ViewB.cshtml
│ └── ViewC.cshtml
│ ├── B
│ ├── ViewA.cshtml
│ ├── ViewB.cstxt
│ └── ViewD.cshtml
│ ├── Cache
│ └── Simple.cshtml
│ ├── Compiler
│ ├── CompiledError.cshtml
│ ├── Dotted.Name.cshtml
│ ├── Layouted.cshtml
│ ├── LayoutedSecondLine.cshtml
│ ├── RazorError.cshtml
│ └── Simple.cshtml
│ └── Renderer
│ ├── Functions.cshtml
│ ├── HierarchicalLayout.cshtml
│ ├── HierarchicalLayout1.cshtml
│ ├── HierarchicalLayout2.cshtml
│ ├── Layout1.cshtml
│ ├── LayoutModel.cshtml
│ ├── LayoutModel1.cshtml
│ ├── Layouted.cshtml
│ ├── Model.cshtml
│ └── Simple.cshtml
├── LeanCode.IntegrationTests
├── App
│ ├── AppRoles.cs
│ ├── AuthConfig.cs
│ ├── CQRS.cs
│ ├── Entity.cs
│ ├── Meeting.cs
│ ├── Program.cs
│ ├── Startup.cs
│ └── TestDbContext.cs
├── CQRSTests.cs
├── EFRepositoryTests.cs
├── LeanCode.IntegrationTests.csproj
├── PostgresOnlyFactAttribute.cs
├── PushNotificationTokenStoreTests.cs
├── TestApp.cs
├── TestDatabaseConfig.cs
├── TimestampTzTests.cs
└── docker
│ ├── .env
│ ├── Dockerfile
│ ├── Dockerfile.watch
│ └── docker-compose.yml
├── LeanCode.Test.Helpers
├── ExternalServiceFactAttribute.cs
├── IntegrationFactAttribute.cs
├── LeanCode.Test.Helpers.csproj
└── LongRunningFact.cs
├── LeanCode.TestCoverage.Tests
├── LeanCode.TestCoverage.Tests.csproj
└── TestCoverageTests.cs
├── Testing
└── LeanCode.IntegrationTestHelpers.Tests
│ ├── App
│ ├── AuthQuery.cs
│ ├── Command.cs
│ ├── ConnectionKeeper.cs
│ ├── Program.cs
│ ├── Query.cs
│ ├── Startup.cs
│ └── TestDbContext.cs
│ ├── LeanCode.IntegrationTestHelpers.Tests.csproj
│ ├── TestApp.cs
│ ├── TestConnectionStringTests.cs
│ └── Tests.cs
├── Tools
└── LeanCode.CodeAnalysis.Tests
│ ├── Analyzers
│ ├── CancellationTokensShouldFollowNamingConventionTests.cs
│ ├── EnsureCQRSHandlersAreInProperNamespaceTests.cs
│ ├── EnsureCQRSHandlersFollowNamingConventionTests.cs
│ ├── EnsureCommandsQueriesAndOperationsHaveAuthorizersTests.cs
│ ├── EnsureValidatorsFollowNamingConventionTests.cs
│ └── SuggestCommandsHaveValidatorsTests.cs
│ ├── CodeActions
│ ├── AddAuthorizationAttributeCodeActionTests.cs
│ ├── AddCommandValidatorCodeActionTests.cs
│ ├── FixCQRSHandlerNamespaceTests.cs
│ ├── FixCQRSHandlerNamingCodeActionTests.cs
│ ├── FixCancellationTokenNamingCodeActionTests.cs
│ └── FixCommandValidatorNamingCodeActionTests.cs
│ ├── LeanCode.CodeAnalysis.Tests.csproj
│ ├── TestSamples
│ ├── Accepted
│ │ ├── CQRS
│ │ │ ├── Command_handlers.cs
│ │ │ ├── Operation_handlers.cs
│ │ │ ├── Query_handlers.cs
│ │ │ └── Validators.cs
│ │ ├── Contracts
│ │ │ ├── Commands.cs
│ │ │ ├── Operations.cs
│ │ │ └── Queries.cs
│ │ └── Methods.cs
│ ├── Command_validation.cs
│ └── Rejected
│ │ ├── CQRS
│ │ ├── Command_handlers.cs
│ │ ├── Operation_handlers.cs
│ │ ├── Query_handlers.cs
│ │ └── Validators.cs
│ │ ├── Contracts
│ │ ├── Commands.cs
│ │ ├── Operations.cs
│ │ └── Queries.cs
│ │ └── Methods.cs
│ └── Verifiers
│ ├── CodeFixVerifier.cs
│ ├── DiagnosticResult.cs
│ └── DiagnosticVerifier.cs
└── coverage.xml
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "csharpier": {
6 | "version": "0.30.4",
7 | "commands": [
8 | "dotnet-csharpier"
9 | ],
10 | "rollForward": false
11 | },
12 | "dotnet-coverage": {
13 | "version": "17.13.1",
14 | "commands": [
15 | "dotnet-coverage"
16 | ],
17 | "rollForward": false
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/.csharpierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "useTabs": false,
4 | "tabWidth": 4,
5 | "preprocessorSymbolSets": []
6 | }
7 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | [Bb]in/
2 | [Oo]bj/
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.cs diff=csharp
3 | *.jpg binary
4 | *.png binary
5 | *.gif binary
6 | *.pdf diff=astextplain
7 | *.PDF diff=astextplain
8 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "nuget"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | groups:
8 | microsoft:
9 | patterns:
10 | - "Microsoft.*"
11 | other:
12 | patterns:
13 | - "*"
14 | exclude-patterns:
15 | - "Microsoft.*"
16 | - package-ecosystem: "pip"
17 | directory: "/docs"
18 | schedule:
19 | interval: "weekly"
20 | groups:
21 | python-packages:
22 | patterns:
23 | - "*"
24 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: CoreLibrary Docs
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - 'docs/**'
7 |
8 | jobs:
9 | lint-changelog:
10 | name: Lint docs
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Check out code
14 | uses: actions/checkout@v2
15 | - name: Lint
16 | uses: DavidAnson/markdownlint-cli2-action@v13
17 | with:
18 | config: ./docs/.markdownlint.jsonc
19 | globs: 'docs/**/*.md'
20 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file for MkDocs projects
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | version: 2
5 | build:
6 | os: ubuntu-22.04
7 | tools:
8 | python: "3.11"
9 |
10 | mkdocs:
11 | configuration: mkdocs.yml
12 |
13 | python:
14 | install:
15 | - requirements: docs/requirements.txt
16 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "EditorConfig.EditorConfig",
4 | "ms-dotnettools.csharp",
5 | "csharpier.csharpier-vscode"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | ".fake": true,
4 | "**/.git": true,
5 | "**/.DS_Store": true,
6 | "**/bin": true,
7 | "**/obj": true,
8 | "**/node_modules": true,
9 | "packages": true,
10 | "**/Logs": true,
11 | "paket-files": true,
12 | "**/obj/**/en": true,
13 | "**/obj/**/pl": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "command": "dotnet",
4 | "args": [],
5 | "tasks": [
6 | {
7 | "label": "build",
8 | "type": "shell",
9 | "command": "dotnet",
10 | "args": [
11 | "build"
12 | ],
13 | "problemMatcher": "$msCompile",
14 | "group": "build"
15 | },
16 | {
17 | "label": "restore",
18 | "type": "shell",
19 | "command": "dotnet",
20 | "args": [
21 | "restore"
22 | ],
23 | "isBackground": true,
24 | "problemMatcher": "$msCompile"
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | LeanCode
5 | LeanCode Core library
6 |
7 | preview
8 | library
9 | net9.0
10 | portable
11 | true
12 | enable
13 | latest
14 | AllEnabledByDefault
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
--------------------------------------------------------------------------------
/cloud-tests-infrastructure/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 |
4 | # .tfstate files
5 | *.tfstate
6 | *.tfstate.*
7 |
8 | # Crash log files
9 | crash.log
10 | crash.*.log
11 |
12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as
13 | # password, private keys, and other secrets. These should not be part of version
14 | # control as they are data points which are potentially sensitive and subject
15 | # to change depending on the environment.
16 | *.tfvars
17 | *.tfvars.json
18 |
19 | # Ignore override files as they are usually used to override resources locally and so
20 | # are not checked in
21 | override.tf
22 | override.tf.json
23 | *_override.tf
24 | *_override.tf.json
25 |
26 | # Include override files you do wish to add to version control using negated pattern
27 | # !example_override.tf
28 |
29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
30 | # example: *tfplan*
31 |
32 | # Ignore CLI configuration files
33 | .terraformrc
34 | terraform.rc
35 |
--------------------------------------------------------------------------------
/cloud-tests-infrastructure/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.2"
3 |
4 | backend "azurerm" {}
5 |
6 | required_providers {
7 | azuread = {
8 | source = "hashicorp/azuread"
9 | version = "~> 3.0"
10 | }
11 | azurerm = {
12 | source = "hashicorp/azurerm"
13 | version = "~> 4.12"
14 | }
15 | }
16 | }
17 |
18 | provider "azurerm" {
19 | tenant_id = var.tenant_id
20 | subscription_id = var.subscription_id
21 |
22 | features {}
23 | }
24 |
25 | provider "azuread" {
26 | tenant_id = var.tenant_id
27 | }
28 |
29 | resource "azurerm_resource_group" "rg" {
30 | name = var.resource_group_name
31 | location = var.location
32 | }
33 |
34 | resource "azuread_application" "tests" {
35 | display_name = "Core Library Tests"
36 | identifier_uris = ["https://corelibrary-tests.project.lncd.pl"]
37 | }
38 |
39 | resource "azuread_service_principal" "tests" {
40 | client_id = azuread_application.tests.client_id
41 | }
42 |
43 | resource "azuread_service_principal_password" "tests" {
44 | service_principal_id = azuread_service_principal.tests.id
45 | }
46 |
--------------------------------------------------------------------------------
/cloud-tests-infrastructure/outputs.tf:
--------------------------------------------------------------------------------
1 | output "test_envs" {
2 | sensitive = true
3 | value = <<-EOT
4 | CORELIB_TESTS_TENANT_ID="${azuread_service_principal.tests.application_tenant_id}"
5 | CORELIB_TESTS_CLIENT_ID="${azuread_application.tests.client_id}"
6 | CORELIB_TESTS_CLIENT_SECRET="${azuread_service_principal_password.tests.value}"
7 | CORELIB_TESTS_NPGSQL_CONNECTION_STRING='${local.npg_connection_string}'
8 | CORELIB_TESTS_AZURE_BLOB_STORAGE_SERVICE_URI='${azurerm_storage_account.storage.primary_blob_endpoint}'
9 | CORELIB_TESTS_AZURE_TABLE_STORAGE_SERVICE_URI='${azurerm_storage_account.storage.primary_table_endpoint}'
10 | CORELIB_TESTS_AZURE_BLOB_STORAGE_CONTAINER_NAME='${local.blob_storage_container_name}'
11 | CORELIB_TESTS_AZURE_TABLE_STORAGE_TABLE_NAME='${local.table_storage_table_name}'
12 | EOT
13 | }
14 |
--------------------------------------------------------------------------------
/cloud-tests-infrastructure/readme.md:
--------------------------------------------------------------------------------
1 | # Cloud tests infrastructure
2 |
3 | Cloud resources used in [LeanCode.Azure.Tests](../test/Infrastructure/LeanCode.Azure.Tests/LeanCode.Azure.Tests.csproj).
4 |
5 | All the necessary env variables are set in `test_credentials` output.
6 |
--------------------------------------------------------------------------------
/cloud-tests-infrastructure/variables.tf:
--------------------------------------------------------------------------------
1 | variable "tenant_id" {
2 | type = string
3 | }
4 |
5 | variable "subscription_id" {
6 | type = string
7 | }
8 |
9 | variable "resource_group_name" {
10 | type = string
11 | }
12 |
13 | variable "location" {
14 | type = string
15 | }
16 |
17 | variable "postgres_server_name" {
18 | type = string
19 | }
20 |
21 | variable "storage_name" {
22 | type = string
23 | }
24 |
--------------------------------------------------------------------------------
/docs/.markdownlint.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "default": true,
3 | // Line length
4 | "MD013": false,
5 | // Unordered list indentation
6 | "MD007": {
7 | "indent": 4
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/docs/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "davidanson.vscode-markdownlint",
4 | "streetsidesoftware.code-spell-checker"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/docs/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "CQRS",
4 | "READMEs",
5 | "appsettings",
6 | "destructurer",
7 | "destructurers",
8 | "msbuild",
9 | "nuget",
10 | "nupkg",
11 | "typeof"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/docs/img/leancode_logo_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/docs/img/leancode_logo_black.png
--------------------------------------------------------------------------------
/docs/img/leancode_logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/docs/img/leancode_logo_white.png
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Babel==2.16.0
2 | certifi==2024.8.30
3 | charset-normalizer==3.4.0
4 | click==8.1.7
5 | colorama==0.4.6
6 | ghp-import==2.1.0
7 | idna==3.10
8 | Jinja2==3.1.4
9 | Markdown==3.7
10 | MarkupSafe==3.0.2
11 | mergedeep==1.3.4
12 | mkdocs==1.6.1
13 | mkdocs-material==9.5.47
14 | mkdocs-material-extensions==1.3.1
15 | packaging==24.2
16 | paginate==0.5.7
17 | pathspec==0.12.1
18 | platformdirs==4.3.6
19 | Pygments==2.18.0
20 | pymdown-extensions==10.12
21 | python-dateutil==2.9.0.post0
22 | PyYAML==6.0.2
23 | pyyaml_env_tag==0.1
24 | regex==2024.11.6
25 | requests==2.32.3
26 | six==1.16.0
27 | urllib3==2.2.3
28 | watchdog==6.0.0
29 |
--------------------------------------------------------------------------------
/docs/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* Primary color shades */
3 | --md-primary-fg-color: #000000;
4 | /* Accent color shades */
5 | --md-accent-fg-color: #c28f02;
6 | --md-typeset-a-color: #c28f02;
7 | }
8 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.100",
4 | "allowPrerelease": false,
5 | "rollForward": "latestMinor"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.CQRS.Annotations.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 |
5 |
6 |
7 |
8 | %(Filename)%(Extension)
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.Contracts.pb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/src/CQRS/LeanCode.CQRS.Annotations/LeanCode.Contracts.pb
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Annotations/PathAliasAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Annotations;
2 |
3 | ///
4 | /// Register command/query/operation under additional HTTP path. This could be useful for reorganizing contracts namespaces without introducing a breaking change.
5 | /// This attribute can be used multiple times.
6 | ///
7 | ///
8 | ///
9 | /// namespace LncdApp.Contracts;
10 | /// {
11 | /// [PathAlias("LncdApp.Contacts.Commands")]
12 | /// public class MyCommand : ICommand { }
13 | /// }
14 | ///
15 | ///
16 | /// MyCommand class will be registered as 'cqrs-base/command/LncdApp.Contracts.MyCommand' and 'cqrs-base/command/LncdApp.Contracts.Commands.MyCommand'
17 | ///
18 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
19 | public sealed class PathAliasAttribute : Attribute
20 | {
21 | public string Path { get; }
22 |
23 | public PathAliasAttribute(string path)
24 | {
25 | Path = path;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Internals.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("LeanCode.CQRS.AspNetCore.Tests")]
4 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/LeanCode.CQRS.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/LocalCallLifetimeFeature.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http.Features;
2 |
3 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
4 |
5 | internal class LocalCallLifetimeFeature : IHttpRequestLifetimeFeature, IDisposable
6 | {
7 | private readonly CancellationTokenSource source;
8 | private CancellationToken requestAborted;
9 |
10 | public CancellationToken RequestAborted
11 | {
12 | get => requestAborted;
13 | set => requestAborted = value;
14 | }
15 |
16 | public CancellationToken CallAborted => source.Token;
17 |
18 | public LocalCallLifetimeFeature(CancellationToken cancellationToken)
19 | {
20 | source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
21 | requestAborted = source.Token;
22 | }
23 |
24 | public void Abort() => source.Cancel();
25 |
26 | public void Dispose() => source.Dispose();
27 | }
28 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/LocalCallServiceProvidersFeature.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http.Features;
2 |
3 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
4 |
5 | internal class LocalCallServiceProvidersFeature : IServiceProvidersFeature
6 | {
7 | public IServiceProvider RequestServices { get; set; }
8 |
9 | public LocalCallServiceProvidersFeature(IServiceProvider requestServices)
10 | {
11 | RequestServices = requestServices;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/NullEndpointFeature.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.AspNetCore.Http.Features;
3 |
4 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
5 |
6 | internal class NullEndpointFeature : IEndpointFeature
7 | {
8 | public static readonly NullEndpointFeature Empty = new();
9 |
10 | public Endpoint? Endpoint
11 | {
12 | get => null;
13 | set { }
14 | }
15 |
16 | private NullEndpointFeature() { }
17 | }
18 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/NullRequestCookieCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
6 |
7 | internal class NullRequestCookieCollection : IRequestCookieCollection
8 | {
9 | public static readonly NullRequestCookieCollection Empty = new();
10 |
11 | public string? this[string key] => null;
12 |
13 | public int Count => 0;
14 |
15 | public ICollection Keys => [];
16 |
17 | private NullRequestCookieCollection() { }
18 |
19 | public bool ContainsKey(string key) => false;
20 |
21 | public IEnumerator> GetEnumerator() =>
22 | Enumerable.Empty>().GetEnumerator();
23 |
24 | public bool TryGetValue(string key, [NotNullWhen(true)] out string? value)
25 | {
26 | value = null;
27 | return false;
28 | }
29 |
30 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
31 | }
32 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/NullResponseCookies.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 |
3 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
4 |
5 | internal class NullResponseCookies : IResponseCookies
6 | {
7 | public static readonly NullResponseCookies Empty = new();
8 |
9 | private NullResponseCookies() { }
10 |
11 | public void Append(string key, string value) { }
12 |
13 | public void Append(string key, string value, CookieOptions options) { }
14 |
15 | public void Delete(string key) { }
16 |
17 | public void Delete(string key, CookieOptions options) { }
18 | }
19 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/NullSession.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
5 |
6 | internal class NullSession : ISession
7 | {
8 | public static readonly NullSession Empty = new();
9 |
10 | public bool IsAvailable => false;
11 |
12 | public string Id => "";
13 |
14 | public IEnumerable Keys => [];
15 |
16 | private NullSession() { }
17 |
18 | public void Clear() { }
19 |
20 | public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
21 |
22 | public Task LoadAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
23 |
24 | public void Remove(string key) { }
25 |
26 | public void Set(string key, byte[] value) { }
27 |
28 | public bool TryGetValue(string key, [NotNullWhen(true)] out byte[]? value)
29 | {
30 | value = null;
31 | return false;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/Context/NullWebSocketManager.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Net.WebSockets;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Local.Context;
6 |
7 | internal class NullWebSocketManager : WebSocketManager
8 | {
9 | public static readonly NullWebSocketManager Empty = new();
10 |
11 | public override bool IsWebSocketRequest => false;
12 |
13 | public override IList WebSocketRequestedProtocols { get; } = new ReadOnlyCollection([]);
14 |
15 | private NullWebSocketManager() { }
16 |
17 | public override Task AcceptWebSocketAsync(string? subProtocol) =>
18 | throw new NotSupportedException("WebSockets are not supported in local CQRS calls.");
19 | }
20 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/ILocalCommandExecutor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using LeanCode.Contracts;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Local;
6 |
7 | public interface ILocalCommandExecutor
8 | {
9 | Task RunAsync(T command, ClaimsPrincipal user, CancellationToken cancellationToken = default)
10 | where T : ICommand;
11 |
12 | Task RunAsync(
13 | T command,
14 | ClaimsPrincipal user,
15 | IHeaderDictionary headers,
16 | CancellationToken cancellationToken = default
17 | )
18 | where T : ICommand;
19 | }
20 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/ILocalOperationExecutor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using LeanCode.Contracts;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Local;
6 |
7 | public interface ILocalOperationExecutor
8 | {
9 | Task ExecuteAsync(
10 | IOperation query,
11 | ClaimsPrincipal user,
12 | CancellationToken cancellationToken = default
13 | );
14 |
15 | Task ExecuteAsync(
16 | IOperation query,
17 | ClaimsPrincipal user,
18 | IHeaderDictionary headers,
19 | CancellationToken cancellationToken = default
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/ILocalQueryExecutor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using LeanCode.Contracts;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Local;
6 |
7 | public interface ILocalQueryExecutor
8 | {
9 | Task GetAsync(
10 | IQuery query,
11 | ClaimsPrincipal user,
12 | CancellationToken cancellationToken = default
13 | );
14 |
15 | Task GetAsync(
16 | IQuery query,
17 | ClaimsPrincipal user,
18 | IHeaderDictionary headers,
19 | CancellationToken cancellationToken = default
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/UnauthenticatedCQRSRequestException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.AspNetCore.Local;
2 |
3 | public class UnauthenticatedCQRSRequestException : Exception
4 | {
5 | public Type ObjectType { get; }
6 |
7 | public UnauthenticatedCQRSRequestException(Type objectType)
8 | : base($"The request {objectType.FullName} was not authenticated.")
9 | {
10 | ObjectType = objectType;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/UnauthorizedCQRSRequestException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.AspNetCore.Local;
2 |
3 | public class UnauthorizedCQRSRequestException : Exception
4 | {
5 | public Type ObjectType { get; }
6 |
7 | public UnauthorizedCQRSRequestException(Type objectType)
8 | : base($"The request {objectType.FullName} was not authorized.")
9 | {
10 | ObjectType = objectType;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Local/UnknownStatusCodeException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.AspNetCore.Local;
2 |
3 | public class UnknownStatusCodeException : Exception
4 | {
5 | public int StatusCode { get; }
6 | public Type ObjectType { get; }
7 |
8 | public UnknownStatusCodeException(int statusCode, Type objectType)
9 | : base($"Unknown status code {statusCode} for request {objectType.FullName}.")
10 | {
11 | StatusCode = statusCode;
12 | ObjectType = objectType;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Middleware/CQRSPipelineFinalizer.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Execution;
2 | using LeanCode.OpenTelemetry;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Middleware;
6 |
7 | internal static class CQRSPipelineFinalizer
8 | {
9 | public static async Task HandleAsync(HttpContext context)
10 | {
11 | var metadata = context.GetCQRSObjectMetadata();
12 | var payload = context.GetCQRSRequestPayload();
13 |
14 | using var activity = LeanCodeActivitySource.StartMiddleware("Execution");
15 |
16 | var result = await metadata.ObjectExecutor(context, payload);
17 |
18 | payload.SetResult(ExecutionResult.WithPayload(result));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Middleware/NonProductionResponseLoggerMiddleware.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Execution;
2 | using LeanCode.Logging;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.Extensions.Hosting;
5 |
6 | namespace LeanCode.CQRS.AspNetCore.Middleware;
7 |
8 | public class NonProductionResponseLoggerMiddleware
9 | {
10 | private readonly IHostEnvironment hostEnvironment;
11 | private readonly ILogger logger;
12 |
13 | public NonProductionResponseLoggerMiddleware(
14 | IHostEnvironment hostEnvironment,
15 | ILogger logger
16 | )
17 | {
18 | this.hostEnvironment = hostEnvironment;
19 | this.logger = logger;
20 | }
21 |
22 | public async Task InvokeAsync(HttpContext httpContext, RequestDelegate next)
23 | {
24 | await next(httpContext);
25 |
26 | var payload = httpContext.GetCQRSRequestPayload();
27 |
28 | if (!hostEnvironment.IsProduction())
29 | {
30 | logger.Information("Request executed with response {@Response}", payload.Result);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Middleware/ResponseLoggerMiddleware.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Execution;
2 | using LeanCode.Logging;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Middleware;
6 |
7 | public class ResponseLoggerMiddleware
8 | {
9 | private readonly RequestDelegate next;
10 | private readonly ILogger logger;
11 |
12 | public ResponseLoggerMiddleware(RequestDelegate next, ILogger logger)
13 | {
14 | this.next = next;
15 | this.logger = logger;
16 | }
17 |
18 | public async Task InvokeAsync(HttpContext httpContext)
19 | {
20 | await next(httpContext);
21 | var result = httpContext.GetCQRSRequestPayload().Result;
22 | logger.Information("Request executed with response {@Response}", result);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/ApiDescriptionTags.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Execution;
2 | using Microsoft.AspNetCore.Routing;
3 |
4 | namespace LeanCode.CQRS.AspNetCore.Registration;
5 |
6 | public static class ApiDescriptionTags
7 | {
8 | public static IReadOnlyList FullNamespace(RouteEndpoint _, CQRSObjectMetadata m)
9 | {
10 | return [(m.ObjectType.Namespace ?? "").Replace(".", " - ", StringComparison.InvariantCulture)];
11 | }
12 |
13 | public static Func> SkipNamespacePrefix(int skipFirst)
14 | {
15 | return (_, m) =>
16 | {
17 | var parts = (m.ObjectType.Namespace ?? "").Split('.').Skip(skipFirst);
18 | return [string.Join(" - ", parts)];
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionConfiguration.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Execution;
2 | using Microsoft.AspNetCore.Routing;
3 |
4 | namespace LeanCode.CQRS.AspNetCore.Registration;
5 |
6 | public class CQRSApiDescriptionConfiguration
7 | {
8 | public Func> TagsMapping { get; init; } =
9 | ApiDescriptionTags.FullNamespace;
10 |
11 | public Func SummaryMapping { get; init; } =
12 | static (_, m) => m.ObjectType.FullName ?? "";
13 |
14 | public Func DescriptionMapping { get; init; } =
15 | static (_, m) => $"Executed by {m.HandlerType.FullName ?? "unknown handler"}";
16 | }
17 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/EndpointMetadata.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http.Metadata;
2 |
3 | namespace LeanCode.CQRS.AspNetCore.Registration;
4 |
5 | internal class EndpointTags(IReadOnlyList tags) : ITagsMetadata
6 | {
7 | public IReadOnlyList Tags => tags;
8 | }
9 |
10 | internal class EndpointSummary(string summary) : IEndpointSummaryMetadata
11 | {
12 | public string Summary => summary;
13 | }
14 |
15 | internal class EndpointDescription(string description) : IEndpointDescriptionMetadata
16 | {
17 | public string Description => description;
18 | }
19 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/ICQRSObjectSource.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Execution;
2 |
3 | namespace LeanCode.CQRS.AspNetCore.Registration;
4 |
5 | public interface ICQRSObjectSource
6 | {
7 | CQRSObjectMetadata MetadataFor(Type type);
8 | }
9 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/CQRSRequestPayload.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Execution;
2 |
3 | public class CQRSRequestPayload
4 | {
5 | public object Payload { get; }
6 |
7 | public ExecutionResult? Result { get; private set; }
8 |
9 | public CQRSRequestPayload(object payload)
10 | {
11 | Payload = payload;
12 | }
13 |
14 | public void SetResult(ExecutionResult? result)
15 | {
16 | if (Result is not null)
17 | {
18 | throw new InvalidOperationException("The request has been already set.");
19 | }
20 |
21 | Result = result;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/CommandExecutionInvalidException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Execution;
2 |
3 | ///
4 | /// Use to propagate arbitrary from the command handler.
5 | /// Use as a last resort - when validation and execution are so coupled that separating it is too complicated.
6 | ///
7 | public class CommandExecutionInvalidException : Exception
8 | {
9 | public int ErrorCode { get; private set; }
10 |
11 | public CommandExecutionInvalidException(int errorCode, string errorMessage, Exception? innerException = null)
12 | : base(errorMessage, innerException)
13 | {
14 | ErrorCode = errorCode;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/CommandHandlerNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Execution;
2 |
3 | public class CommandHandlerNotFoundException : Exception
4 | {
5 | public Type CommandType { get; }
6 |
7 | public CommandHandlerNotFoundException(Type commandType)
8 | : base($"Cannot find handler for command {commandType.Name}.")
9 | {
10 | CommandType = commandType;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/ExecutionResult.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Execution;
2 |
3 | public readonly record struct ExecutionResult
4 | {
5 | public int StatusCode { get; private init; }
6 |
7 | ///
8 | /// Indicates that result should be written in http response, however may still be a null value.
9 | ///
10 | public bool HasPayload { get; private init; }
11 | public object? Payload { get; private init; }
12 |
13 | public static ExecutionResult Empty(int code) => new() { StatusCode = code };
14 |
15 | public static ExecutionResult WithPayload(object? payload, int code = 200) =>
16 | new()
17 | {
18 | StatusCode = code,
19 | HasPayload = true,
20 | Payload = payload,
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/ICommandHandler.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace LeanCode.CQRS.Execution;
5 |
6 | public interface ICommandHandler
7 | where TCommand : ICommand
8 | {
9 | Task ExecuteAsync(HttpContext context, TCommand command);
10 | }
11 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/IOperationHandler.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace LeanCode.CQRS.Execution;
5 |
6 | public interface IOperationHandler
7 | where TOperation : IOperation
8 | {
9 | Task ExecuteAsync(HttpContext context, TOperation operation);
10 | }
11 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/IQueryHandler.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace LeanCode.CQRS.Execution;
5 |
6 | public interface IQueryHandler
7 | where TQuery : IQuery
8 | {
9 | Task ExecuteAsync(HttpContext context, TQuery query);
10 | }
11 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/LeanCode.CQRS.Execution.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/OperationHandlerNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Execution;
2 |
3 | public class OperationHandlerNotFoundException : Exception
4 | {
5 | public OperationHandlerNotFoundException(Type operationType)
6 | : base($"Cannot find handler for operation {operationType.Name}.")
7 | {
8 | OperationType = operationType;
9 | }
10 |
11 | public Type OperationType { get; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Execution/QueryHandlerNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Execution;
2 |
3 | public class QueryHandlerNotFoundException : Exception
4 | {
5 | public Type QueryType { get; }
6 |
7 | public QueryHandlerNotFoundException(Type queryType)
8 | : base($"Cannot find handler for query {queryType.Name}.")
9 | {
10 | QueryType = queryType;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.MassTransitRelay/AsyncEventsInterceptorExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Model;
2 |
3 | namespace LeanCode.CQRS.MassTransitRelay;
4 |
5 | public static class AsyncEventsInterceptorExtensions
6 | {
7 | public static async Task<(TResult Result, List Events)> CaptureEventsOfAsync(
8 | this AsyncEventsInterceptor interceptor,
9 | Func> action
10 | )
11 | {
12 | interceptor.Prepare();
13 | var result = await action();
14 | var interceptedEvents = interceptor.CaptureQueue()?.ToList() ?? new List();
15 |
16 | return (result, interceptedEvents);
17 | }
18 |
19 | public static async Task> CaptureEventsOfAsync(
20 | this AsyncEventsInterceptor interceptor,
21 | Func action
22 | )
23 | {
24 | interceptor.Prepare();
25 | await action();
26 | var interceptedEvents = interceptor.CaptureQueue()?.ToList() ?? new List();
27 |
28 | return interceptedEvents;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.MassTransitRelay/BusObserverExtensions.cs:
--------------------------------------------------------------------------------
1 | using MassTransit;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace LeanCode.CQRS.MassTransitRelay;
5 |
6 | public static class BusObserverExtensions
7 | {
8 | public static void ConnectBusObservers(this IBusObserverConnector bus, IBusRegistrationContext context)
9 | {
10 | var observers = context.GetServices();
11 | foreach (var obs in observers)
12 | {
13 | bus.ConnectBusObserver(obs);
14 | }
15 | }
16 |
17 | public static void ConnectReceiveEndpointObservers(
18 | this IReceiveEndpointObserverConnector rcv,
19 | IBusRegistrationContext context
20 | )
21 | {
22 | var recvObservers = context.GetServices();
23 | foreach (var obs in recvObservers)
24 | {
25 | rcv.ConnectReceiveEndpointObserver(obs);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.MassTransitRelay/LeanCode.CQRS.MassTransitRelay.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.MassTransitRelay/MassTransitRelayApplicationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.MassTransitRelay.Middleware;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace LeanCode.CQRS.MassTransitRelay;
6 |
7 | public static class MassTransitRelayApplicationBuilderExtensions
8 | {
9 | public static IApplicationBuilder CommitTransaction(this IApplicationBuilder builder)
10 | where TDbContext : DbContext
11 | {
12 | builder.UseMiddleware>();
13 | return builder;
14 | }
15 |
16 | public static IApplicationBuilder PublishEvents(this IApplicationBuilder builder)
17 | {
18 | builder.UseMiddleware();
19 | return builder;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.MassTransitRelay/Middleware/CommitDatabaseTransactionMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LeanCode.CQRS.MassTransitRelay.Middleware;
5 |
6 | public class CommitDatabaseTransactionMiddleware
7 | where TDbContext : DbContext
8 | {
9 | private readonly RequestDelegate next;
10 |
11 | public CommitDatabaseTransactionMiddleware(RequestDelegate next)
12 | {
13 | this.next = next;
14 | }
15 |
16 | public async Task InvokeAsync(HttpContext httpContext, TDbContext dbContext)
17 | {
18 | await next(httpContext);
19 | await dbContext.SaveChangesAsync(httpContext.RequestAborted);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.MassTransitRelay/Testing/ResettableBusActivityMonitorInitializer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 |
3 | namespace LeanCode.CQRS.MassTransitRelay.Testing;
4 |
5 | internal sealed class ResettableBusActivityMonitorInitializer : IHostedService
6 | {
7 | public ResettableBusActivityMonitorInitializer(ResettableBusActivityMonitor monitor)
8 | {
9 | // ServiceCollection does not allow any auto-initialize of services,
10 | // so we just want to instantiate it somehow, to start observing the bus
11 | _ = monitor;
12 | }
13 |
14 | public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
15 |
16 | public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
17 | }
18 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.RemoteHttp.Client/HttpResponseMessageExtensions.cs:
--------------------------------------------------------------------------------
1 | using static System.Net.HttpStatusCode;
2 |
3 | namespace LeanCode.CQRS.RemoteHttp.Client;
4 |
5 | public static class HttpResponseMessageExtensions
6 | {
7 | public static void HandleCommonCQRSErrors(this HttpResponseMessage response)
8 | where TNotFound : Exception, new()
9 | where TBadRequest : Exception, new()
10 | {
11 | switch (response.StatusCode)
12 | {
13 | case NotFound:
14 | throw new TNotFound();
15 | case BadRequest:
16 | throw new TBadRequest();
17 | case InternalServerError:
18 | throw new InternalServerErrorException();
19 | case Unauthorized:
20 | throw new UnauthorizedException();
21 | case Forbidden:
22 | throw new ForbiddenException();
23 | }
24 |
25 | if (!response.IsSuccessStatusCode)
26 | {
27 | throw new HttpCallErrorException(response.StatusCode);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.RemoteHttp.Client/LeanCode.CQRS.RemoteHttp.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.ScopedTransaction/LeanCode.CQRS.ScopedTransaction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.ScopedTransaction/PessimisticConcurrencyAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LeanCode.CQRS.ScopedTransaction;
5 |
6 | [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
7 | public sealed class PessimisticConcurrencyAttribute : Attribute
8 | {
9 | ///
10 | /// Whether should explicitly call .
11 | ///
12 | public bool SaveChanges { get; }
13 | public IsolationLevel IsolationLevel { get; }
14 |
15 | public PessimisticConcurrencyAttribute(
16 | IsolationLevel isolationLevel = IsolationLevel.ReadCommitted,
17 | bool saveChanges = true
18 | )
19 | {
20 | SaveChanges = saveChanges;
21 | IsolationLevel = isolationLevel;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/ClaimsPrincipalExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace LeanCode.CQRS.Security;
4 |
5 | public static class ClaimsPrincipalExtensions
6 | {
7 | public static bool HasPermission(
8 | this ClaimsPrincipal claimsPrincipal,
9 | RoleRegistry registry,
10 | params string[] permissions
11 | )
12 | {
13 | return registry.All.Any(role =>
14 | claimsPrincipal.IsInRole(role.Name) && permissions.Any(role.Permissions.Contains)
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/CustomAuthorizerNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Security;
2 |
3 | public class CustomAuthorizerNotFoundException : Exception
4 | {
5 | public Type AuthorizerType { get; }
6 |
7 | public CustomAuthorizerNotFoundException(Type authorizerType)
8 | : base($"Cannot find custom authorizer for {authorizerType.Name}.")
9 | {
10 | AuthorizerType = authorizerType;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/Exceptions/InsufficientPermissionException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Security.Exceptions;
2 |
3 | public class InsufficientPermissionException : Exception
4 | {
5 | public string? AuthorizerName { get; private set; }
6 |
7 | public InsufficientPermissionException(string message, string? authorizerName)
8 | : base(message)
9 | {
10 | AuthorizerName = authorizerName;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/Exceptions/UnauthenticatedException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Security.Exceptions;
2 |
3 | public class UnauthenticatedException : Exception
4 | {
5 | public UnauthenticatedException(string message)
6 | : base(message) { }
7 | }
8 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/IAuthorizerResolver.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Security;
2 |
3 | public interface IAuthorizerResolver
4 | {
5 | ICustomAuthorizerWrapper? FindAuthorizer(Type authorizerType, Type objectType);
6 | }
7 |
8 | public interface ICustomAuthorizerWrapper
9 | {
10 | Type UnderlyingAuthorizer { get; }
11 | Task CheckIfAuthorizedAsync(object appContext, object obj, object? customData);
12 | }
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/IRoleRegistration.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Security;
2 |
3 | public interface IRoleRegistration
4 | {
5 | IEnumerable Roles { get; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/LeanCode.CQRS.Security.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/Role.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 |
3 | namespace LeanCode.CQRS.Security;
4 |
5 | public class Role
6 | {
7 | public string Name { get; }
8 | public ImmutableHashSet Permissions { get; }
9 |
10 | public Role(string name, params string[] permissions)
11 | {
12 | Validate(name);
13 |
14 | Name = name;
15 | Permissions = ImmutableHashSet.Create(permissions);
16 | }
17 |
18 | public Role(string name, IEnumerable permissions)
19 | {
20 | Validate(name);
21 |
22 | Name = name;
23 | Permissions = permissions.ToImmutableHashSet();
24 | }
25 |
26 | private static void Validate(string name)
27 | {
28 | if (string.IsNullOrWhiteSpace(name))
29 | {
30 | throw new ArgumentException("Name must be specified.", nameof(name));
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Security/RoleRegistry.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 |
3 | namespace LeanCode.CQRS.Security;
4 |
5 | public sealed class RoleRegistry
6 | {
7 | public ImmutableList All { get; }
8 |
9 | public RoleRegistry(IEnumerable registrations)
10 | {
11 | All = registrations.SelectMany(r => r.Roles).ToImmutableList();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation.Fluent/FluentValidationErrorState.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.Validation.Fluent;
2 |
3 | public sealed class FluentValidatorErrorState
4 | {
5 | public int ErrorCode { get; }
6 |
7 | public FluentValidatorErrorState(int code)
8 | {
9 | ErrorCode = code;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation.Fluent/FluentValidationExtensions.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using FluentValidation.Results;
3 |
4 | namespace LeanCode.CQRS.Validation.Fluent;
5 |
6 | public static class FluentValidatorExtensions
7 | {
8 | public static IRuleBuilderOptions WithCode(
9 | this IRuleBuilderOptions rule,
10 | int code
11 | )
12 | {
13 | return rule.WithState(_ => new FluentValidatorErrorState(code));
14 | }
15 |
16 | public static void AddValidationError(
17 | this ValidationContext ctx,
18 | string errorMessage,
19 | int errorCode,
20 | string? propertyName = null
21 | )
22 | {
23 | errorMessage = ctx.MessageFormatter.BuildMessage(errorMessage);
24 | ctx.AddFailure(
25 | new ValidationFailure(propertyName ?? ctx.PropertyPath, errorMessage)
26 | {
27 | CustomState = new FluentValidatorErrorState(errorCode),
28 | }
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation.Fluent/LeanCode.CQRS.Validation.Fluent.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation.Fluent/ServiceProviderExtensions.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using LeanCode.Components;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace LeanCode.CQRS.Validation.Fluent;
6 |
7 | public static class ServiceProviderExtensions
8 | {
9 | public static IServiceCollection AddFluentValidation(
10 | this IServiceCollection serviceCollection,
11 | TypesCatalog catalog,
12 | ServiceLifetime validatorsLifetime = ServiceLifetime.Scoped
13 | )
14 | {
15 | serviceCollection.AddScoped(typeof(ICommandValidator<>), typeof(AdapterLoader<>));
16 | serviceCollection.RegisterGenericTypes(catalog, typeof(IValidator<>), validatorsLifetime);
17 |
18 | return serviceCollection;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation.Fluent/ValidationContextExtensions.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace LeanCode.CQRS.Validation.Fluent;
6 |
7 | public static class ValidationContextExtensions
8 | {
9 | public const string HttpContextKey = "HttpContext";
10 |
11 | public static HttpContext HttpContext(this IValidationContext ctx)
12 | {
13 | if (ctx.RootContextData.TryGetValue(HttpContextKey, out var httpContext))
14 | {
15 | return (HttpContext)httpContext;
16 | }
17 | else
18 | {
19 | throw new InvalidOperationException(
20 | "`HttpContext` is not available in validation context. Ensure you are calling the validator through FluentValidationCommandValidatorAdapter."
21 | );
22 | }
23 | }
24 |
25 | public static T GetService(this IValidationContext ctx)
26 | where T : notnull
27 | {
28 | return ctx.HttpContext().RequestServices.GetRequiredService();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation/ICommandValidator.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.Contracts.Validation;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.Validation;
6 |
7 | public interface ICommandValidator
8 | where TCommand : ICommand
9 | {
10 | Task ValidateAsync(HttpContext httpContext, TCommand command);
11 | }
12 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation/ICommandValidatorResolver.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.Contracts.Validation;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.Validation;
6 |
7 | public interface ICommandValidatorResolver
8 | {
9 | ICommandValidatorWrapper? FindCommandValidator(Type commandType);
10 | }
11 |
12 | ///
13 | /// Marker interface, do not use directly.
14 | ///
15 | public interface ICommandValidatorWrapper
16 | {
17 | Task ValidateAsync(HttpContext appContext, ICommand command);
18 | }
19 |
--------------------------------------------------------------------------------
/src/CQRS/LeanCode.CQRS.Validation/LeanCode.CQRS.Validation.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Core/LeanCode.Components.Autofac/IAppModule.cs:
--------------------------------------------------------------------------------
1 | using Autofac;
2 | using Autofac.Core;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace LeanCode.Components.Autofac;
6 |
7 | public interface IAppModule : IModule
8 | {
9 | void ConfigureServices(IServiceCollection services);
10 | }
11 |
12 | public class AppModule : Module, IAppModule
13 | {
14 | public virtual void ConfigureServices(IServiceCollection services) { }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Core/LeanCode.Components.Autofac/LeanCode.Components.Autofac.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Core/LeanCode.Components/LeanCode.Components.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Core/LeanCode.Startup.MicrosoftDI/LeanCode.Startup.MicrosoftDI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/Core/LeanCode.Startup.MicrosoftDI/LeanProgram.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Configuration;
3 | using Microsoft.Extensions.Hosting;
4 |
5 | namespace LeanCode.Startup.MicrosoftDI;
6 |
7 | public static class LeanProgram
8 | {
9 | public const string SystemLoggersEntryName = "Serilog:SystemLoggers";
10 |
11 | public static IHostBuilder BuildMinimalHost()
12 | where TStartup : class
13 | {
14 | return new HostBuilder()
15 | .ConfigureAppConfiguration(
16 | (_, config) =>
17 | {
18 | config.AddEnvironmentVariables();
19 | }
20 | )
21 | .ConfigureWebHost(builder =>
22 | {
23 | builder
24 | .UseKestrel(
25 | (builderContext, options) =>
26 | {
27 | options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
28 | }
29 | )
30 | .UseStartup();
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Core/LeanCode.Startup.MicrosoftDI/LeanStartup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.Configuration;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.Extensions.Hosting;
5 | using Serilog;
6 |
7 | namespace LeanCode.Startup.MicrosoftDI;
8 |
9 | public abstract class LeanStartup
10 | {
11 | protected IConfiguration Configuration { get; }
12 |
13 | protected virtual bool CloseAndFlushLogger { get; } = true;
14 |
15 | protected LeanStartup(IConfiguration config)
16 | {
17 | Configuration = config;
18 | }
19 |
20 | public abstract void ConfigureServices(IServiceCollection services);
21 |
22 | public void Configure(IApplicationBuilder app)
23 | {
24 | ConfigureApp(app);
25 |
26 | if (CloseAndFlushLogger)
27 | {
28 | var appLifetime = app.ApplicationServices.GetRequiredService();
29 | appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
30 | }
31 | }
32 |
33 | protected abstract void ConfigureApp(IApplicationBuilder app);
34 | }
35 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.EF/DbContextExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Model;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LeanCode.DomainModels.EF;
5 |
6 | public static class DbContextExtensions
7 | {
8 | public static void SoftDeleteItems(this DbContext dbContext)
9 | {
10 | var softDeletables = dbContext
11 | .ChangeTracker.Entries()
12 | .Where(p => p.State == EntityState.Deleted && p.Entity is ISoftDeletable);
13 |
14 | foreach (var entry in softDeletables)
15 | {
16 | entry.State = EntityState.Modified;
17 | entry.CurrentValues[nameof(ISoftDeletable.IsDeleted)] = true;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.EF/LeanCode.DomainModels.EF.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.EF/QueryExpressionVisitorInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 | using Microsoft.EntityFrameworkCore.Diagnostics;
3 |
4 | namespace LeanCode.DomainModels.EF;
5 |
6 | public sealed class QueryExpressionVisitorInterceptor(ExpressionVisitor visitor) : IQueryExpressionInterceptor
7 | {
8 | public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData) =>
9 | visitor.Visit(queryExpression);
10 | }
11 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.EF/SimpleEFRepository.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Model;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LeanCode.DomainModels.EF;
5 |
6 | public sealed class SimpleEFRepository : EFRepository
7 | where TEntity : class, IAggregateRoot
8 | where TIdentity : IEquatable
9 | where TContext : DbContext
10 | {
11 | public SimpleEFRepository(TContext dbContext)
12 | : base(dbContext) { }
13 |
14 | public override Task FindAsync(TIdentity id, CancellationToken cancellationToken = default) =>
15 | DbSet.AsTracking().FirstOrDefaultAsync(e => e.Id.Equals(id), cancellationToken);
16 | }
17 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.Generators/AnalyzerReleases.Shipped.md:
--------------------------------------------------------------------------------
1 | # Release 1.0
2 |
3 | ## New Rules
4 |
5 | Rule ID -| Category | Severity | Notes
6 | ---------|----------|----------|--------------------
7 | LNCD0005 | Domain | Error | TypedIdGenerator
8 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.Generators/LeanCode.DomainModels.Generators.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.Generators/TypedIdData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace LeanCode.DomainModels.Generators;
4 |
5 | public sealed class TypedIdData
6 | {
7 | public TypedIdFormat Format { get; }
8 | public string Namespace { get; }
9 | public string TypeName { get; }
10 | public string? CustomPrefix { get; }
11 | public bool SkipRandomGenerator { get; }
12 | public bool IsValid { get; }
13 | public Location? Location { get; }
14 |
15 | public TypedIdData(
16 | TypedIdFormat format,
17 | string @namespace,
18 | string typeName,
19 | string? customSlug,
20 | bool skipRandomGenerator,
21 | bool isValid,
22 | Location? location
23 | )
24 | {
25 | Format = format;
26 | Namespace = @namespace;
27 | TypeName = typeName;
28 | CustomPrefix = customSlug;
29 | SkipRandomGenerator = skipRandomGenerator;
30 | IsValid = isValid;
31 | Location = location;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels.Generators/TypedIdFormat.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Generators;
2 |
3 | public enum TypedIdFormat
4 | {
5 | RawInt = 0,
6 | RawLong = 1,
7 | RawGuid = 2,
8 | PrefixedGuid = 3,
9 | PrefixedUlid = 4,
10 | }
11 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/DataAccess/IRepository.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Model;
2 |
3 | namespace LeanCode.DomainModels.DataAccess;
4 |
5 | public interface IRepository
6 | where TEntity : class, IAggregateRootWithoutOptimisticConcurrency
7 | where TIdentity : notnull
8 | {
9 | Task FindAsync(TIdentity id, CancellationToken cancellationToken = default);
10 |
11 | void Add(TEntity entity);
12 | void Delete(TEntity entity);
13 | void DeleteRange(IEnumerable entities);
14 | void Update(TEntity entity);
15 | }
16 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/LeanCode.DomainModels.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/DomainEvents.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace LeanCode.DomainModels.Model;
4 |
5 | public static class DomainEvents
6 | {
7 | public static IDomainEventInterceptor? EventInterceptor { get; private set; }
8 |
9 | public static void SetInterceptor(IDomainEventInterceptor interceptor)
10 | {
11 | EventInterceptor = interceptor;
12 | }
13 |
14 | [SuppressMessage("?", "CA1030", Justification = "Convention for `DomainEvents`.")]
15 | public static void Raise(TEvent domainEvent)
16 | where TEvent : class, IDomainEvent
17 | {
18 | EventInterceptor?.Intercept(domainEvent);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/IAggregateRoot.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public interface IAggregateRoot
4 | : IAggregateRootWithoutOptimisticConcurrency,
5 | IOptimisticConcurrency
6 | where TIdentity : notnull { }
7 |
8 | public interface IAggregateRootWithoutOptimisticConcurrency
9 | : IEntity,
10 | IAggregateRootWithoutOptimisticConcurrency
11 | where TIdentity : notnull { }
12 |
13 | [System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
14 | public interface IAggregateRootWithoutOptimisticConcurrency { }
15 |
16 | [Obsolete("`IIdentifiable` is obsolete. Use `IEntity` instead.")]
17 | public interface IIdentifiable : IEntity
18 | where TIdentity : notnull { }
19 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/IDomainEvent.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public interface IDomainEvent
4 | {
5 | Guid Id { get; }
6 | DateTime DateOccurred { get; }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/IDomainEventInterceptor.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public interface IDomainEventInterceptor
4 | {
5 | void Intercept(IDomainEvent domainEvent);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/IEntity.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public interface IEntity
4 | where TIdentity : notnull
5 | {
6 | TIdentity Id { get; }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/IOptimisticConcurrency.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public interface IOptimisticConcurrency
4 | {
5 | DateTime DateModified { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/ISoftDeletable.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public interface ISoftDeletable
4 | {
5 | bool IsDeleted { get; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.DomainModels/Model/ValueObject.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.DomainModels.Model;
2 |
3 | public abstract record ValueObject;
4 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.TimeProvider/LeanCode.TimeProvider.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.TimeProvider/Time.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.TimeProvider;
2 |
3 | public static class Time
4 | {
5 | public static System.TimeProvider Provider { get; set; } = UtcSystemTimeProvider.Instance;
6 |
7 | public static DateTime UtcNow => NowWithOffset.UtcDateTime;
8 | public static DateTime Now => NowWithOffset.DateTime;
9 | public static DateTimeOffset NowWithOffset => Provider.GetLocalNow();
10 | }
11 |
--------------------------------------------------------------------------------
/src/Domain/LeanCode.TimeProvider/UtcSystemTimeProvider.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.TimeProvider;
2 |
3 | public sealed class UtcSystemTimeProvider : System.TimeProvider
4 | {
5 | public static UtcSystemTimeProvider Instance { get; } = new();
6 |
7 | public override TimeZoneInfo LocalTimeZone => TimeZoneInfo.Utc;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UrlHelper/LeanCode.UrlHelper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UrlHelper/UrlHelper.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode;
2 |
3 | public static class UrlHelper
4 | {
5 | public static string Concat(string a, string b)
6 | {
7 | return a.TrimEnd('/') + '/' + b.TrimStart('/');
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UserIdExtractors/Extractors/GuidUserIdExtractor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace LeanCode.UserIdExtractors.Extractors;
4 |
5 | public sealed class GuidUserIdExtractor : IUserIdExtractor
6 | {
7 | private readonly string userIdClaim;
8 |
9 | public GuidUserIdExtractor(string userIdClaim)
10 | {
11 | this.userIdClaim = userIdClaim;
12 | }
13 |
14 | public Guid Extract(ClaimsPrincipal user)
15 | {
16 | var claim = user.FindFirst(userIdClaim)?.Value;
17 | ArgumentException.ThrowIfNullOrEmpty(claim);
18 |
19 | return Guid.Parse(claim);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UserIdExtractors/Extractors/PrefixedTypedUserIdExtractor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using LeanCode.DomainModels.Ids;
3 |
4 | namespace LeanCode.UserIdExtractors.Extractors;
5 |
6 | public sealed class PrefixedTypedUserIdExtractor : IUserIdExtractor
7 | where TId : struct, IPrefixedTypedId
8 | {
9 | private readonly string userIdClaim;
10 |
11 | public PrefixedTypedUserIdExtractor(string userIdClaim)
12 | {
13 | this.userIdClaim = userIdClaim;
14 | }
15 |
16 | public TId Extract(ClaimsPrincipal user)
17 | {
18 | var claim = user.FindFirst(userIdClaim)?.Value;
19 | ArgumentException.ThrowIfNullOrEmpty(claim);
20 |
21 | return TId.Parse(claim);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UserIdExtractors/Extractors/StringUserIdExtractor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace LeanCode.UserIdExtractors.Extractors;
4 |
5 | public class StringUserIdExtractor : IUserIdExtractor
6 | {
7 | private readonly string userIdClaim;
8 |
9 | public StringUserIdExtractor(string userIdClaim)
10 | {
11 | this.userIdClaim = userIdClaim;
12 | }
13 |
14 | public string Extract(ClaimsPrincipal user)
15 | {
16 | var claim = user.FindFirst(userIdClaim)?.Value;
17 | ArgumentException.ThrowIfNullOrEmpty(claim);
18 |
19 | return claim;
20 | }
21 | }
22 |
23 | public sealed class GenericStringUserIdExtractor : StringUserIdExtractor, IUserIdExtractor
24 | {
25 | public GenericStringUserIdExtractor(string userIdClaim)
26 | : base(userIdClaim) { }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UserIdExtractors/IUserIdExtractor.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace LeanCode.UserIdExtractors;
4 |
5 | public interface IUserIdExtractor : IUserIdExtractor
6 | where TUserId : IEquatable
7 | {
8 | new TUserId Extract(ClaimsPrincipal user);
9 | string IUserIdExtractor.Extract(ClaimsPrincipal user) => Extract(user).ToString()!;
10 | }
11 |
12 | public interface IUserIdExtractor
13 | {
14 | string Extract(ClaimsPrincipal user);
15 | }
16 |
--------------------------------------------------------------------------------
/src/Helpers/LeanCode.UserIdExtractors/LeanCode.UserIdExtractors.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AI.Contracts/LeanCode.AI.Contracts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AI.Contracts/McpToolAttribute.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 |
3 | namespace LeanCode.AI.Contracts;
4 |
5 | [ExcludeFromContractsGeneration]
6 | [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
7 | public sealed class McpToolAttribute(string description) : Attribute
8 | {
9 | public string Description { get; } = description;
10 | public string? Name { get; init; }
11 | public string? Title { get; init; }
12 | public McpToolHints Hints { get; init; } = McpToolHints.Default;
13 | }
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AI.Contracts/McpToolHints.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 |
3 | namespace LeanCode.AI.Contracts;
4 |
5 | [ExcludeFromContractsGeneration]
6 | [Flags]
7 | public enum McpToolHints
8 | {
9 | None = 0,
10 | Default = 1,
11 |
12 | Destructive = 0b10,
13 | Idempotent = 0b100,
14 | OpenWorld = 0b1000,
15 | ReadOnly = 0b10000,
16 | }
17 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AI.McpServer/LeanCode.AI.McpServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/AppicationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LeanCode.AuditLogs;
5 |
6 | public static class ApplicationBuilderExtensions
7 | {
8 | public static IApplicationBuilder Audit(this IApplicationBuilder builder)
9 | where TDbContext : DbContext
10 | {
11 | builder.UseMiddleware>();
12 | return builder;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/AuditLogsConsumer.cs:
--------------------------------------------------------------------------------
1 | using MassTransit;
2 |
3 | namespace LeanCode.AuditLogs;
4 |
5 | public class AuditLogsConsumer : IConsumer
6 | {
7 | private readonly IAuditLogStorage auditLogStorage;
8 |
9 | public AuditLogsConsumer(IAuditLogStorage auditLogStorage)
10 | {
11 | this.auditLogStorage = auditLogStorage;
12 | }
13 |
14 | public Task Consume(ConsumeContext context)
15 | {
16 | return auditLogStorage.StoreEventAsync(context.Message, context.CancellationToken);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/AuditLogsExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.AuditLogs;
2 |
3 | namespace Microsoft.Extensions.DependencyInjection;
4 |
5 | public static class AuditLogsExtensions
6 | {
7 | public static IServiceCollection AddAzureStorageAuditLogs(
8 | this IServiceCollection services,
9 | AzureBlobAuditLogStorageConfiguration config
10 | )
11 | {
12 | services.AddSingleton(config);
13 | services.AddTransient();
14 | services.AddTransient();
15 | return services;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/AuditLogsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using MassTransit;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace LeanCode.AuditLogs;
6 |
7 | public class AuditLogsMiddleware
8 | where TDbContext : DbContext
9 | {
10 | private readonly RequestDelegate next;
11 |
12 | public AuditLogsMiddleware(RequestDelegate next)
13 | {
14 | this.next = next;
15 | }
16 |
17 | public async Task InvokeAsync(
18 | HttpContext httpContext,
19 | TDbContext dbContext,
20 | IBus bus,
21 | AuditLogsPublisher auditLogsPublisher
22 | )
23 | {
24 | await next(httpContext);
25 | await auditLogsPublisher.ExtractAndPublishAsync(
26 | dbContext,
27 | bus,
28 | httpContext.Request.Path.ToString(),
29 | httpContext.RequestAborted
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/EntityData.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | namespace LeanCode.AuditLogs;
4 |
5 | public record EntityData(
6 | IReadOnlyList Ids,
7 | string Type,
8 | JsonDocument Changes,
9 | JsonDocument ShadowProperties,
10 | string EntityState
11 | );
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/IAuditLogStorage.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.AuditLogs;
2 |
3 | public interface IAuditLogStorage
4 | {
5 | Task StoreEventAsync(AuditLogMessage auditLogMessage, CancellationToken cancellationToken);
6 | }
7 |
8 | public record AuditLogMessage(
9 | EntityData EntityChanged,
10 | string ActionName,
11 | DateTimeOffset DateOccurred,
12 | string? ActorId,
13 | string? TraceId,
14 | string? SpanId
15 | );
16 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AuditLogs/MassTransintRegistrationConfigurationExtensions.cs:
--------------------------------------------------------------------------------
1 | using MassTransit;
2 |
3 | namespace LeanCode.AuditLogs;
4 |
5 | public static class MassTransitRegistrationConfigurationExtensions
6 | {
7 | public static void AddAuditLogsConsumer(this IRegistrationConfigurator configurator)
8 | {
9 | configurator.AddConsumer(typeof(AuditLogsConsumer), typeof(AuditLogsConsumerDefinition));
10 | }
11 |
12 | public static void AddAuditLogsConsumer(this IRegistrationConfigurator configurator)
13 | {
14 | configurator.AddConsumer(typeof(AuditLogsConsumer), typeof(T));
15 | }
16 | }
17 |
18 | public class AuditLogsConsumerDefinition : ConsumerDefinition
19 | {
20 | protected override void ConfigureConsumer(
21 | IReceiveEndpointConfigurator endpointConfigurator,
22 | IConsumerConfigurator consumerConfigurator,
23 | IRegistrationContext context
24 | )
25 | {
26 | endpointConfigurator.UseMessageRetry(r =>
27 | r.Immediate(1).Incremental(3, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5))
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AzureIdentity/AzureCredentialConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.AzureIdentity;
2 |
3 | public record AzureCredentialConfiguration
4 | {
5 | public string? TenantId { get; init; }
6 | public string? ClientId { get; init; }
7 | public string? ClientSecret { get; init; }
8 | public bool UseManagedIdentity { get; init; }
9 | public bool UseAzureCLI { get; init; }
10 | public bool UseAzureWorkloadIdentity { get; init; }
11 |
12 | public static string TenantIdKey { get; set; } = "Azure:TenantId";
13 | public static string ClientIdKey { get; set; } = "Azure:ClientId";
14 | public static string ClientSecretKey { get; set; } = "Azure:ClientSecret";
15 | public static string UseManagedIdentityKey { get; set; } = "Azure:UseManagedIdentity";
16 | public static string UseAzureCLIKey { get; set; } = "Azure:UseAzureCLI";
17 | public static string UseAzureWorkloadIdentityKey { get; set; } = "Azure:UseAzureWorkloadIdentity";
18 | }
19 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.AzureIdentity/LeanCode.AzureIdentity.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ClientCredentialsHandler/ClientCredentialsConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ClientCredentialsHandler;
2 |
3 | public class ClientCredentialsConfiguration
4 | {
5 | public string ServerAddress { get; set; } = "";
6 | public string ClientId { get; set; } = "";
7 | public string ClientSecret { get; set; } = "";
8 | public string Scopes { get; set; } = "";
9 | }
10 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ClientCredentialsHandler/LeanCode.ClientCredentialsHandler.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ConfigCat/ConfigCatInitializer.cs:
--------------------------------------------------------------------------------
1 | using ConfigCat.Client;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace LeanCode.ConfigCat;
5 |
6 | public sealed class ConfigCatInitializer : BackgroundService
7 | {
8 | private readonly IConfigCatClient configCatClient;
9 |
10 | public ConfigCatInitializer(IConfigCatClient configCatClient)
11 | {
12 | this.configCatClient = configCatClient;
13 | }
14 |
15 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
16 | {
17 | await configCatClient.GetAllKeysAsync(stoppingToken);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ConfigCat/ConfigCatOptions.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ConfigCat;
2 |
3 | public sealed record class ConfigCatOptions(
4 | string? SdkKey,
5 | string? FlagOverridesFilePath,
6 | string? FlagOverridesJsonObject
7 | )
8 | {
9 | public ConfigCatOptions()
10 | : this(default, default, default) { }
11 | };
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ConfigCat/LeanCode.ConfigCat.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Dapper/LeanCode.Dapper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Dapper/RawSqlQueryAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Dapper;
2 |
3 | [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
4 | public sealed class RawSqlQueryAttribute : Attribute { }
5 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.EFMigrator/LeanCode.EFMigrator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.FCM/FCMSendException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Firebase.FCM;
2 |
3 | public class FCMSendException : Exception
4 | {
5 | public FCMSendException()
6 | : base("There was an error with one or more push notifications.") { }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.FCM/IPushNotificationTokenStore.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Firebase.FCM;
2 |
3 | public interface IPushNotificationTokenStore
4 | where TUserId : IEquatable
5 | {
6 | const int MaxTokenBatchSize = 100;
7 |
8 | Task> GetTokensAsync(TUserId userId, CancellationToken cancellationToken = default);
9 |
10 | Task>> GetTokensAsync(
11 | IReadOnlySet userIds,
12 | CancellationToken cancellationToken = default
13 | );
14 |
15 | Task AddUserTokenAsync(TUserId userId, string newToken, CancellationToken cancellationToken = default);
16 | Task RemoveUserTokenAsync(TUserId userId, string token, CancellationToken cancellationToken = default);
17 |
18 | Task RemoveTokenAsync(string token, CancellationToken cancellationToken = default);
19 | Task RemoveTokensAsync(IEnumerable tokens, CancellationToken cancellationToken = default);
20 | Task RemoveAllUserTokensAsync(TUserId userId, CancellationToken cancellationToken = default);
21 | }
22 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.FCM/LeanCode.Firebase.FCM.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.FCM/ModelBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace LeanCode.Firebase.FCM;
4 |
5 | public static class ModelBuilderExtensions
6 | {
7 | public static void ConfigurePushNotificationTokenEntity(
8 | this ModelBuilder builder,
9 | bool setTokenColumnMaxLength
10 | )
11 | where TUserId : IEquatable
12 | {
13 | builder.Entity>(c =>
14 | {
15 | c.HasKey(e => new { e.UserId, e.Token });
16 | c.HasIndex(e => e.Token).IsUnique();
17 |
18 | c.Property(e => e.UserId).ValueGeneratedNever();
19 |
20 | var t = c.Property(e => e.Token).ValueGeneratedNever();
21 |
22 | if (setTokenColumnMaxLength)
23 | {
24 | // https://stackoverflow.com/q/39959417
25 | t.HasMaxLength(512);
26 | }
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.FCM/Notifications.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Firebase.FCM;
2 |
3 | public static class Notifications
4 | {
5 | private static readonly NotificationDataConverter SharedConverter = NotificationDataConverter.New().Build();
6 |
7 | ///
8 | /// Converts POCO object to a notification data dictionary. Does not support hierarchical
9 | /// data.
10 | ///
11 | [Obsolete("Use `NotificationDataConverter.ToNotificationData` instead` ")]
12 | public static Dictionary ToNotificationData(object data)
13 | {
14 | return SharedConverter.ToNotificationData(data);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.FCM/PushNotificationTokenEntity.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Firebase.FCM;
2 |
3 | public sealed record class PushNotificationTokenEntity(TUserId UserId, string Token, DateTime DateCreated)
4 | where TUserId : IEquatable;
5 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.Firestore/FirestoreModule.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.DependencyInjection.Extensions;
3 | using Microsoft.Extensions.Hosting;
4 |
5 | namespace LeanCode.Firebase.Firestore;
6 |
7 | public static class FirestoreServiceProviderExtensions
8 | {
9 | public static void AddFirestore(this IServiceCollection services)
10 | {
11 | services.TryAddSingleton();
12 | services.AddSingleton(sp => sp.GetRequiredService());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase.Firestore/LeanCode.Firebase.Firestore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Firebase/LeanCode.Firebase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.Contracts.pb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.Contracts.pb
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ForceUpdate.Contracts/LeanCode.ForceUpdate.Contracts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | annotations
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | %(Filename)%(Extension)
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ForceUpdate.Contracts/VersionSupport.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 |
3 | namespace LeanCode.ForceUpdate.Contracts;
4 |
5 | public class VersionSupport : IQuery
6 | {
7 | public PlatformDTO Platform { get; set; }
8 | public string Version { get; set; }
9 | }
10 |
11 | public class VersionSupportDTO
12 | {
13 | public string CurrentlySupportedVersion { get; set; }
14 | public string MinimumRequiredVersion { get; set; }
15 | public VersionSupportResultDTO Result { get; set; }
16 | }
17 |
18 | public enum PlatformDTO
19 | {
20 | Android = 0,
21 | IOS = 1,
22 | }
23 |
24 | public enum VersionSupportResultDTO
25 | {
26 | UpdateRequired = 0,
27 | UpdateSuggested = 1,
28 | UpToDate = 2,
29 | }
30 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ForceUpdate/CQRSServicesBuilderCQRSExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Components;
2 | using LeanCode.CQRS.AspNetCore;
3 | using LeanCode.ForceUpdate.Contracts;
4 | using LeanCode.ForceUpdate.Services;
5 | using LeanCode.ForceUpdate.Services.CQRS;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace LeanCode.ForceUpdate;
9 |
10 | public static class CQRSServicesBuilderCQRSExtensions
11 | {
12 | public static CQRSServicesBuilder AddForceUpdate(
13 | this CQRSServicesBuilder cqrsServicesBuilder,
14 | AndroidVersionsConfiguration androidConfiguration,
15 | IOSVersionsConfiguration iOSConfiguration
16 | )
17 | {
18 | cqrsServicesBuilder.Services.AddTransient();
19 |
20 | cqrsServicesBuilder.Services.AddSingleton(androidConfiguration);
21 |
22 | cqrsServicesBuilder.Services.AddSingleton(iOSConfiguration);
23 |
24 | return cqrsServicesBuilder.AddCQRSObjects(
25 | TypesCatalog.Of(),
26 | TypesCatalog.Of()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ForceUpdate/LeanCode.ForceUpdate.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ForceUpdate/VersionsConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ForceUpdate;
2 |
3 | public sealed record IOSVersionsConfiguration(Version MinimumRequiredVersion, Version CurrentlySupportedVersion);
4 |
5 | public sealed record AndroidVersionsConfiguration(Version MinimumRequiredVersion, Version CurrentlySupportedVersion);
6 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Kratos/KratosAuthenticationOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using LeanCode.Kratos.Client.Model;
3 | using Microsoft.AspNetCore.Authentication;
4 |
5 | namespace LeanCode.Kratos;
6 |
7 | public class KratosAuthenticationOptions : AuthenticationSchemeOptions
8 | {
9 | public bool AllowInactiveIdentities { get; set; }
10 | public string SessionCookieName { get; set; } = KratosDefaults.SessionCookieName;
11 | public string NameClaimType { get; set; } = null!;
12 | public string RoleClaimType { get; set; } = null!;
13 | public Action> ClaimsExtractor { get; set; } = null!;
14 | }
15 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Kratos/KratosDefaults.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Kratos;
2 |
3 | public static class KratosDefaults
4 | {
5 | public const string AuthenticationScheme = "Kratos";
6 | public const string SessionCookieName = "ory_kratos_session";
7 | }
8 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Kratos/LeanCode.Kratos.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Localization/LeanCode.Localization.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Localization/LocalizationConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Localization;
2 |
3 | public sealed class LocalizationConfiguration
4 | {
5 | public Type ResourceSource { get; }
6 |
7 | public LocalizationConfiguration(Type resourceSource)
8 | {
9 | ResourceSource = resourceSource;
10 | }
11 |
12 | public static LocalizationConfiguration For() => new LocalizationConfiguration(typeof(T));
13 | }
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Localization/LocalizationServiceProviderExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Localization.StringLocalizers;
2 | using LeanCode.Logging;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace LeanCode.Localization;
6 |
7 | public static class LocalizationServiceProviderExtensions
8 | {
9 | public static IServiceCollection AddStringLocalizer(
10 | this IServiceCollection services,
11 | LocalizationConfiguration config
12 | )
13 | {
14 | return services.AddSingleton(sp => new ResourceManagerStringLocalizer(
15 | config,
16 | sp.GetRequiredService>()
17 | ));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Localization/LocalizedResourceNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Localization;
2 |
3 | public class LocalizedResourceNotFoundException : Exception
4 | {
5 | public LocalizedResourceNotFoundException(Exception inner)
6 | : base(inner.Message, inner) { }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Localization/StringLocalizers/IStringLocalizer.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace LeanCode.Localization.StringLocalizers;
4 |
5 | ///
6 | /// Represents a service that provides localized strings for any given culture.
7 | ///
8 | public interface IStringLocalizer
9 | {
10 | ///
11 | /// Gets the string resource with the given name and localized for the specified culture.
12 | ///
13 | /// The to get the string for.
14 | /// The name of the string resource.
15 | /// The localized string.
16 | /// One or both arguments are `null`.
17 | /// Localized resource could not be found.
18 | string this[CultureInfo culture, string name] { get; }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Localization/StringLocalizers/IStringLocalizerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace LeanCode.Localization.StringLocalizers;
4 |
5 | public static class IStringLocalizerExtensions
6 | {
7 | public static string Format(
8 | this IStringLocalizer @this,
9 | CultureInfo culture,
10 | string name,
11 | params object[] arguments
12 | )
13 | {
14 | return string.Format(culture, @this[culture, name], arguments);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging.AspNetCore/LeanCode.Logging.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging.AspNetCore/SerilogExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using Serilog;
3 | using Serilog.Core;
4 |
5 | namespace LeanCode.Logging.AspNetCore;
6 |
7 | internal static class SerilogExtensions
8 | {
9 | internal static LoggerConfiguration DestructureCommonObjects(
10 | this LoggerConfiguration config,
11 | IEnumerable? searchAssemblies
12 | )
13 | {
14 | if (searchAssemblies != null)
15 | {
16 | return SelectTypes(searchAssemblies)
17 | .Aggregate(config, (a, s) => a.Destructure.With(s));
18 | }
19 | else
20 | {
21 | return config;
22 | }
23 | }
24 |
25 | private static List SelectTypes(IEnumerable searchAssemblies)
26 | {
27 | return searchAssemblies
28 | .SelectMany(a => a.ExportedTypes)
29 | .Where(t => typeof(TType).IsAssignableFrom(t) && t.IsPublic && t.GetConstructor(Type.EmptyTypes) != null)
30 | .Select(Activator.CreateInstance)
31 | .Cast()
32 | .ToList();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging/ContextualLogger.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Events;
3 |
4 | namespace LeanCode.Logging;
5 |
6 | public class ContextualLogger(ILogger logger) : ILogger
7 | {
8 | private readonly ILogger logger = logger.ForContext();
9 |
10 | public void Write(LogEvent logEvent) => logger.Write(logEvent);
11 | }
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging/ILogger.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Logging;
2 |
3 | public interface ILogger : Serilog.ILogger
4 | {
5 | public new ILogger ForContext() =>
6 | this is NullLogger ? NullLogger.Instance : new ContextualLogger(this);
7 | }
8 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging/LeanCode.Logging.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging/LeanCodeLoggingBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Logging;
3 |
4 | namespace LeanCode.Logging;
5 |
6 | public static class LeanCodeLoggingBuilderExtensions
7 | {
8 | public static ILoggingBuilder AddContextualLeanCodeLogger(this ILoggingBuilder builder)
9 | {
10 | builder.Services.AddSingleton(typeof(ILogger<>), typeof(ContextualLogger<>));
11 |
12 | return builder;
13 | }
14 |
15 | public static ILoggingBuilder AddNullLeanCodeLogger(this ILoggingBuilder builder)
16 | {
17 | builder.Services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>));
18 |
19 | return builder;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Logging/NullLogger.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 |
3 | namespace LeanCode.Logging;
4 |
5 | public class NullLogger : ILogger
6 | {
7 | public static readonly NullLogger Instance = new();
8 |
9 | public void Write(LogEvent logEvent) { }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Mixpanel/IMixpanelEvent.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Mixpanel;
2 |
3 | public interface IMixpanelEvent
4 | {
5 | string EventName { get; }
6 | Dictionary Properties { get; }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Mixpanel/LeanCode.Mixpanel.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Mixpanel/MixpanelConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.Mixpanel;
2 |
3 | public class MixpanelConfiguration
4 | {
5 | public string Token { get; set; } = "";
6 | public string ApiKey { get; set; } = "";
7 | public bool VerboseErrors { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Mixpanel/MixpanelServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace LeanCode.Mixpanel;
4 |
5 | public static class MixpanelServiceCollectionExtensions
6 | {
7 | public static IServiceCollection AddMixpanel(this IServiceCollection services, MixpanelConfiguration config)
8 | {
9 | services.AddSingleton(config);
10 | services.AddHttpClient(c => c.BaseAddress = new Uri("https://api.mixpanel.com"));
11 | return services;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Npgsql.ActiveDirectory/LeanCode.Npgsql.ActiveDirectory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Npgsql.ActiveDirectory/NpgsqlDataSourceBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Azure.Core;
2 | using Npgsql;
3 |
4 | namespace LeanCode.Npgsql.ActiveDirectory;
5 |
6 | public static class NpgsqlDataSourceBuilderExtensions
7 | {
8 | private static readonly string[] Scopes = new[] { "https://ossrdbms-aad.database.windows.net/.default" };
9 |
10 | public static NpgsqlDataSourceBuilder UseAzureActiveDirectoryAuthentication(
11 | this NpgsqlDataSourceBuilder builder,
12 | TokenCredential credential
13 | )
14 | {
15 | return builder.UsePeriodicPasswordProvider(
16 | async (_, ct) =>
17 | {
18 | var token = await credential.GetTokenAsync(new(Scopes, null), ct);
19 |
20 | return token.Token;
21 | },
22 | TimeSpan.FromMinutes(5),
23 | TimeSpan.FromSeconds(1)
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.OpenTelemetry/LeanCode.OpenTelemetry.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.OpenTelemetry/LeanCodeActivitySource.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace LeanCode.OpenTelemetry;
4 |
5 | public static class LeanCodeActivitySource
6 | {
7 | public static readonly ActivitySource ActivitySource = new("LeanCode.CoreLibrary");
8 |
9 | public static Activity? Start(string name) => ActivitySource.StartActivity(name);
10 |
11 | public static Activity? StartExecution(string type, string? objectType) => Start($"{type} - {objectType}");
12 |
13 | public static Activity? StartMiddleware(string middlewareName) => Start($"middleware - {middlewareName}");
14 |
15 | public static Activity? StartMiddleware(string middlewareName, string? objectName) =>
16 | Start($"middleware - {middlewareName} - {objectName}");
17 | }
18 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.OpenTelemetry/LeanCodeMetrics.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.OpenTelemetry;
2 |
3 | public static class LeanCodeMetrics
4 | {
5 | public const string MeterName = "LeanCode.CoreLibrary";
6 | }
7 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.OpenTelemetry/MeterBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using OpenTelemetry.Metrics;
2 |
3 | namespace LeanCode.OpenTelemetry;
4 |
5 | public static class MeterBuilderExtensions
6 | {
7 | public static MeterProviderBuilder AddLeanCodeInstrumentation(this MeterProviderBuilder builder)
8 | {
9 | return builder.AddMeter(LeanCodeMetrics.MeterName);
10 | }
11 |
12 | [Obsolete($"Use {nameof(AddLeanCodeInstrumentation)} instead.")]
13 | public static MeterProviderBuilder AddLeanCodeMetrics(this MeterProviderBuilder builder)
14 | {
15 | return builder.AddLeanCodeInstrumentation();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.OpenTelemetry/TracerBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using OpenTelemetry.Trace;
2 |
3 | namespace LeanCode.OpenTelemetry;
4 |
5 | public static class TracerProviderBuilderExtensions
6 | {
7 | public static TracerProviderBuilder AddLeanCodeInstrumentation(this TracerProviderBuilder builder)
8 | {
9 | return builder.AddSource(LeanCodeActivitySource.ActivitySource.Name);
10 | }
11 |
12 | [Obsolete($"Use {nameof(AddLeanCodeInstrumentation)} instead.")]
13 | public static TracerProviderBuilder AddLeanCodeTelemetry(this TracerProviderBuilder builder)
14 | {
15 | return builder.AddLeanCodeInstrumentation();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.PeriodicService/IPeriodicAction.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Cronos;
3 |
4 | namespace LeanCode.PeriodicService;
5 |
6 | public interface IPeriodicAction
7 | {
8 | [SuppressMessage("?", "CA1716", Justification = "Convention for `PeriodicAction`.")]
9 | CronExpression When { get; }
10 | bool SkipFirstExecution { get; }
11 |
12 | [SuppressMessage("?", "LNCD0006", Justification = "Convention for `PeriodicAction`.")]
13 | Task ExecuteAsync(CancellationToken stoppingToken);
14 | }
15 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.PeriodicService/LeanCode.PeriodicService.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.PeriodicService/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace LeanCode.PeriodicService;
4 |
5 | public static class ServiceCollectionExtensions
6 | {
7 | public static IServiceCollection RegisterPeriodicAction(this IServiceCollection builder)
8 | where T : class, IPeriodicAction
9 | {
10 | builder.AddTransient();
11 | return builder.AddHostedService>();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SendGrid/LeanCode.SendGrid.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SendGrid/SendGridException.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Net;
3 |
4 | namespace LeanCode.SendGrid;
5 |
6 | public class SendGridException : Exception
7 | {
8 | public HttpStatusCode ResponseStatusCode { get; }
9 | public ImmutableArray ErrorMessages { get; } = ImmutableArray.Empty;
10 |
11 | internal SendGridException(HttpStatusCode responseStatusCode, IEnumerable? errors)
12 | : base($"SendGrid request failed with status code {responseStatusCode}.")
13 | {
14 | ResponseStatusCode = responseStatusCode;
15 |
16 | if (errors is object)
17 | {
18 | ErrorMessages = errors.Select(e => e.Message!).Where(m => m is object).ToImmutableArray();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SendGrid/SendGridRazorMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using SendGrid.Helpers.Mail;
3 |
4 | namespace LeanCode.SendGrid;
5 |
6 | public class SendGridRazorMessage : SendGridMessage
7 | {
8 | [JsonIgnore]
9 | public object? PlainTextContentModel { get; set; }
10 |
11 | [JsonIgnore]
12 | public object? HtmlContentModel { get; set; }
13 |
14 | internal virtual IEnumerable GetTemplateNames(string templateBaseName)
15 | {
16 | yield return templateBaseName;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SendGrid/SendGridResponse.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.SendGrid;
2 |
3 | internal sealed class SendGridResponse
4 | {
5 | public SendGridError[]? Errors { get; set; }
6 | }
7 |
8 | internal sealed class SendGridError
9 | {
10 | public string? Message { get; set; }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SendGrid/SendGridServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.DependencyInjection.Extensions;
3 | using SendGrid;
4 |
5 | namespace LeanCode.SendGrid;
6 |
7 | public static class SendGridServiceCollectionExtensions
8 | {
9 | public static IServiceCollection AddSendGridClient(this IServiceCollection services, SendGridClientOptions config)
10 | {
11 | services.AddSingleton(config);
12 | services.TryAddTransient(s => new SendGridClient(s.GetRequiredService()));
13 | services.TryAddTransient();
14 | return services;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.Serialization/LeanCode.Serialization.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SmsSender/Exceptions/Exceptions.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.SmsSender.Exceptions;
2 |
3 | public abstract class ResponseException : Exception
4 | {
5 | public int ErrorCode { get; private set; }
6 |
7 | protected ResponseException(int errorCode, string message)
8 | : base(message)
9 | {
10 | ErrorCode = errorCode;
11 | }
12 | }
13 |
14 | public class ActionException : ResponseException
15 | {
16 | public ActionException(int errorCode, string message)
17 | : base(errorCode, message) { }
18 | }
19 |
20 | public class ClientException : ResponseException
21 | {
22 | public ClientException(int errorCode, string message)
23 | : base(errorCode, message) { }
24 | }
25 |
26 | public class HostException : ResponseException
27 | {
28 | public HostException(int errorCode, string message)
29 | : base(errorCode, message) { }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SmsSender/ISmsSender.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.SmsSender;
2 |
3 | public interface ISmsSender
4 | {
5 | Task SendAsync(string message, string phoneNumber, CancellationToken cancellationToken = default);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SmsSender/LeanCode.SmsSender.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.SmsSender/SmsSenderServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace LeanCode.SmsSender;
4 |
5 | public static class SmsSenderServiceCollectionExtensions
6 | {
7 | public static IServiceCollection AddSmsSender(this IServiceCollection services, SmsApiConfiguration config)
8 | {
9 | services.AddSingleton(config);
10 | services.AddHttpClient(
11 | (sp, c) =>
12 | {
13 | var config = sp.GetRequiredService();
14 |
15 | SmsApiClient.ConfigureHttpClient(config, c);
16 | }
17 | );
18 |
19 | return services;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/Extensions/Layout.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Razor.Language;
2 |
3 | namespace LeanCode.ViewRenderer.Razor.Extensions;
4 |
5 | internal class Layout
6 | {
7 | public const string DirectiveName = "layout";
8 |
9 | public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateSingleLineDirective(
10 | DirectiveName,
11 | b => b.AddMemberToken()
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/Extensions/LayoutDirectivePass.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Razor.Language;
2 | using Microsoft.AspNetCore.Razor.Language.Intermediate;
3 |
4 | namespace LeanCode.ViewRenderer.Razor.Extensions;
5 |
6 | internal class LayoutDirectivePass : RazorEngineFeatureBase, IRazorDirectiveClassifierPass
7 | {
8 | public int Order => 1001;
9 |
10 | public void Execute(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
11 | {
12 | var @class = documentNode.FindPrimaryClass();
13 |
14 | var layout = documentNode
15 | .FindDirectiveReferences(Layout.Directive)
16 | .SelectMany(d => ((DirectiveIntermediateNode)d.Node).Tokens)
17 | .FirstOrDefault(t => t != null);
18 |
19 | var prop = new LayoutNode(layout?.Content);
20 |
21 | @class.Children.Add(prop);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/Internals.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | // NSubsitute
4 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
5 | [assembly: InternalsVisibleTo("LeanCode.ViewRenderer.Razor.Tests")]
6 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/LeanCode.ViewRenderer.Razor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/RazorViewRendererOptions.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ViewRenderer.Razor;
2 |
3 | public class RazorViewRendererOptions
4 | {
5 | public const string DefaultExtension = ".cshtml";
6 |
7 | public IReadOnlyList ViewLocations { get; }
8 | public string Extension { get; }
9 |
10 | public RazorViewRendererOptions(IReadOnlyList viewLocations, string extension = DefaultExtension)
11 | {
12 | ViewLocations = viewLocations;
13 | Extension = extension;
14 | }
15 |
16 | public RazorViewRendererOptions(params string[] viewLocations)
17 | {
18 | ViewLocations = viewLocations;
19 | Extension = DefaultExtension;
20 | }
21 |
22 | public RazorViewRendererOptions(string extension, string[] viewLocations)
23 | {
24 | ViewLocations = viewLocations;
25 | Extension = extension;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/RazorViewRendererServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Logging;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace LeanCode.ViewRenderer.Razor;
5 |
6 | public static class RazorViewRendererServiceCollectionExtensions
7 | {
8 | public static IServiceCollection AddRazorViewRenderer(
9 | this IServiceCollection services,
10 | RazorViewRendererOptions config
11 | )
12 | {
13 | return services.AddSingleton(sp => new RazorViewRenderer(
14 | config,
15 | sp.GetRequiredService>()
16 | ));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/ViewBase/AttributeValue.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ViewRenderer.Razor.ViewBase;
2 |
3 | public class AttributeValue
4 | {
5 | public string Prefix { get; }
6 | public object Value { get; }
7 | public bool Literal { get; }
8 |
9 | public AttributeValue(string prefix, object value, bool literal)
10 | {
11 | Prefix = prefix;
12 | Value = value;
13 | Literal = literal;
14 | }
15 |
16 | public static AttributeValue FromTuple(Tuple value) =>
17 | new AttributeValue(value.Item1, value.Item2, value.Item3);
18 |
19 | public static AttributeValue FromTuple(Tuple value) =>
20 | new AttributeValue(value.Item1, value.Item2, value.Item3);
21 |
22 | public static implicit operator AttributeValue(Tuple value) => FromTuple(value);
23 | }
24 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer.Razor/ViewBase/HelperResult.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ViewRenderer.Razor.ViewBase;
2 |
3 | public class HelperResult
4 | {
5 | public Action WriteAction { get; }
6 |
7 | public HelperResult(Action action)
8 | {
9 | WriteAction = action;
10 | }
11 |
12 | public void WriteTo(TextWriter writer)
13 | {
14 | WriteAction(writer);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer/IViewRenderer.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ViewRenderer;
2 |
3 | public interface IViewRenderer
4 | {
5 | ///
6 | /// is generally faster alternative, use that if possible.
7 | ///
8 | /// should be a public type because currently
9 | /// the (only) implementation based on Razor uses dynamic types and the object binder
10 | /// respects the visibility.
11 | ///
12 | Task RenderToStringAsync(string viewName, object model, CancellationToken cancellationToken = default);
13 |
14 | ///
15 | /// should be a public type because currently
16 | /// the (only) implementation based on Razor uses dynamic types and the object binder
17 | /// respects the visibility.
18 | ///
19 | Task RenderToStreamAsync(
20 | string viewName,
21 | object model,
22 | Stream outputStream,
23 | CancellationToken cancellationToken = default
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer/LeanCode.ViewRenderer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/Infrastructure/LeanCode.ViewRenderer/ViewNotFoundException.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.ViewRenderer;
2 |
3 | public class ViewNotFoundException : Exception
4 | {
5 | public string? ViewName { get; }
6 |
7 | public ViewNotFoundException(string? viewName, string message)
8 | : base(message)
9 | {
10 | ViewName = viewName;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Testing/LeanCode.DomainModels.EventsExecution.TestHelpers/LeanCode.DomainModels.EventsExecution.TestHelpers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Testing/LeanCode.IntegrationTestHelpers/HttpClientExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Headers;
2 | using System.Security.Claims;
3 |
4 | namespace LeanCode.IntegrationTestHelpers;
5 |
6 | public static class HttpClientExtensions
7 | {
8 | public static HttpClient UseTestAuthorization(this HttpClient client, ClaimsPrincipal principal)
9 | {
10 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
11 | TestAuthenticationHandler.SchemeName,
12 | TestAuthenticationHandler.SerializePrincipal(principal)
13 | );
14 | return client;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Testing/LeanCode.TimeProvider.TestHelpers/LeanCode.TimeProvider.TestHelpers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Testing/LeanCode.TimeProvider.TestHelpers/TestTimeProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Time.Testing;
2 |
3 | namespace LeanCode.TimeProvider.TestHelpers;
4 |
5 | public static class TestTimeProvider
6 | {
7 | public static FakeTimeProvider ActivateFake(DateTime epoch, TimeZoneInfo? localTimeZone = null) =>
8 | ActivateFake(new DateTimeOffset(epoch, TimeSpan.Zero), localTimeZone);
9 |
10 | public static FakeTimeProvider ActivateFake(DateTimeOffset epoch, TimeZoneInfo? localTimeZone = null)
11 | {
12 | var provider = new FakeTimeProvider(epoch.ToUniversalTime());
13 |
14 | if (localTimeZone is not null)
15 | {
16 | provider.SetLocalTimeZone(localTimeZone);
17 | }
18 |
19 | AsyncLocalTimeProvider.Activate(provider);
20 |
21 | return provider;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/AnalyzerReleases.Shipped.md:
--------------------------------------------------------------------------------
1 | ## Release 1.0
2 |
3 | ### New Rules
4 | Rule ID -| Category | Severity | Notes
5 | ---------|----------|----------|--------------------
6 | LNCD0001 | Security | Error | EnsureCommandsAndQueriesHaveAuthorizers
7 | LNCD0002 | Security | Error | EnsureCommandsAndQueriesHaveAuthorizers
8 | LNCD0003 | Usage | Info | SuggestCommandsHaveValidators
9 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/CodeFixProviders/AddCommandValidatorCodeFixProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Composition;
3 | using LeanCode.CodeAnalysis.CodeActions;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CodeFixes;
6 |
7 | namespace LeanCode.CodeAnalysis.CodeFixProviders;
8 |
9 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddAuthorizationAttributeCodeFixProvider))]
10 | [Shared]
11 | public class AddCommandValidatorCodeFixProvider : CodeFixProvider
12 | {
13 | public override ImmutableArray FixableDiagnosticIds { get; } =
14 | ImmutableArray.Create(DiagnosticsIds.CommandsShouldHaveValidators);
15 |
16 | public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
17 |
18 | public override Task RegisterCodeFixesAsync(CodeFixContext context)
19 | {
20 | context.RegisterCodeFix(new AddCommandValidatorCodeAction(context.Document, context.Span), context.Diagnostics);
21 |
22 | return Task.CompletedTask;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/CodeFixProviders/FixCQRSHandlerNamespaceCodeFixProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Composition;
3 | using LeanCode.CodeAnalysis.CodeActions;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CodeFixes;
6 |
7 | namespace LeanCode.CodeAnalysis.CodeFixProviders;
8 |
9 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FixCQRSHandlerNamespaceCodeFixProvider))]
10 | [Shared]
11 | public class FixCQRSHandlerNamespaceCodeFixProvider : CodeFixProvider
12 | {
13 | public override ImmutableArray FixableDiagnosticIds { get; } =
14 | ImmutableArray.Create(DiagnosticsIds.CQRSHandlersShouldBeInProperNamespace);
15 |
16 | public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
17 |
18 | public override Task RegisterCodeFixesAsync(CodeFixContext context)
19 | {
20 | context.RegisterCodeFix(
21 | new FixCQRSHandlerNamespaceCodeAction(context.Document, context.Span),
22 | context.Diagnostics
23 | );
24 |
25 | return Task.CompletedTask;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/CodeFixProviders/FixCancellationTokenNamingCodeFixProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Composition;
3 | using LeanCode.CodeAnalysis.CodeActions;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CodeFixes;
6 |
7 | namespace LeanCode.CodeAnalysis.CodeFixProviders;
8 |
9 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FixCancellationTokenNamingCodeFixProvider))]
10 | [Shared]
11 | public class FixCancellationTokenNamingCodeFixProvider : CodeFixProvider
12 | {
13 | public override ImmutableArray FixableDiagnosticIds { get; } =
14 | ImmutableArray.Create(DiagnosticsIds.CancellationTokensShouldFollowNamingConvention);
15 |
16 | public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
17 |
18 | public override Task RegisterCodeFixesAsync(CodeFixContext context)
19 | {
20 | context.RegisterCodeFix(
21 | new FixCancellationTokenNamingCodeAction(context.Document, context.Span),
22 | context.Diagnostics
23 | );
24 |
25 | return Task.CompletedTask;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/CodeFixProviders/FixCommandValidatorNamingCodeFixProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Composition;
3 | using LeanCode.CodeAnalysis.CodeActions;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CodeFixes;
6 |
7 | namespace LeanCode.CodeAnalysis.CodeFixProviders;
8 |
9 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FixCommandValidatorNamingCodeFixProvider))]
10 | [Shared]
11 | public class FixCommandValidatorNamingCodeFixProvider : CodeFixProvider
12 | {
13 | public override ImmutableArray FixableDiagnosticIds { get; } =
14 | ImmutableArray.Create(DiagnosticsIds.CommandValidatorsShouldFollowNamingConvention);
15 |
16 | public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
17 |
18 | public override Task RegisterCodeFixesAsync(CodeFixContext context)
19 | {
20 | context.RegisterCodeFix(
21 | new FixCommandValidatorNamingCodeAction(context.Document, context.Span),
22 | context.Diagnostics
23 | );
24 |
25 | return Task.CompletedTask;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/DiagnosticsIds.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CodeAnalysis;
2 |
3 | public static class DiagnosticsIds
4 | {
5 | public const string CommandsShouldHaveAuthorizers = "LNCD0001";
6 | public const string QueriesShouldHaveAuthorizers = "LNCD0002";
7 | public const string CommandsShouldHaveValidators = "LNCD0003";
8 | public const string OperationsShouldHaveAuthorizers = "LNCD0004";
9 | public const string TypedIdMustBeStruct = "LNCD0005";
10 | public const string CancellationTokensShouldFollowNamingConvention = "LNCD0006";
11 | public const string CommandHandlersShouldFollowNamingConvention = "LNCD0007";
12 | public const string QueryHandlersShouldFollowNamingConvention = "LNCD0008";
13 | public const string OperationHandlersShouldFollowNamingConvention = "LNCD0009";
14 | public const string CommandValidatorsShouldFollowNamingConvention = "LNCD0010";
15 | public const string CQRSHandlersShouldBeInProperNamespace = "LNCD0011";
16 | }
17 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/LeanCode.CodeAnalysis.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | true
6 | true
7 |
8 |
9 | netstandard2.1
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Tools/LeanCode.CodeAnalysis/local-debug.txt:
--------------------------------------------------------------------------------
1 | For playing with code analyzers without referencing it as a nuget package,
2 | reference the project in the following way.
3 |
4 |
5 | false
6 | Analyzer
7 |
8 |
--------------------------------------------------------------------------------
/test-bed/Api/TestQuery.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.CQRS.Execution;
3 |
4 | namespace LeanCode.TestBed.Api;
5 |
6 | [Obsolete("For tests.")]
7 | public class TestQuery : IQuery { }
8 |
9 | public class TestQueryResult
10 | {
11 | public Guid Id { get; set; }
12 | public string Property1 { get; set; } = default!;
13 | public TestQueryResult? Inner { get; set; }
14 | }
15 |
16 | [Obsolete("For tests.")]
17 | public class TestQueryQH : IQueryHandler
18 | {
19 | public Task ExecuteAsync(HttpContext context, TestQuery query)
20 | {
21 | return Task.FromResult(
22 | new TestQueryResult
23 | {
24 | Id = Guid.NewGuid(),
25 | Property1 = "Some value",
26 | Inner = new() { Id = Guid.NewGuid(), Property1 = "Other value" },
27 | }
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test-bed/Program.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Components;
2 | using LeanCode.CQRS.AspNetCore;
3 | using LeanCode.CQRS.Validation.Fluent;
4 | using LeanCode.TestBed.Api;
5 | using Serilog;
6 |
7 | var builder = WebApplication.CreateBuilder(args);
8 |
9 | builder.Logging.AddSerilog();
10 | builder.Services.AddFluentValidation(TypesCatalog.Of());
11 | builder.Services.AddCQRS(TypesCatalog.Of(), TypesCatalog.Of());
12 | builder.Services.AddCQRSApiExplorer();
13 | builder.Services.AddOpenApi();
14 |
15 | var app = builder.Build();
16 |
17 | app.MapOpenApi();
18 | app.MapRemoteCQRS(
19 | "/",
20 | c =>
21 | {
22 | c.Commands = p => p.Secure().Validate();
23 | c.Queries = p => p.Secure();
24 | }
25 | );
26 | app.UseReDoc(opt =>
27 | {
28 | opt.SpecUrl("/openapi/v1.json");
29 | opt.RoutePrefix = "redoc";
30 | });
31 | app.UseSwaggerUI(opt =>
32 | {
33 | opt.SwaggerEndpoint("/openapi/v1.json", "Test API");
34 | opt.RoutePrefix = "swagger";
35 | });
36 |
37 | app.Run();
38 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests.Integration/CustomAuthorizer.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Security.Claims;
3 | using LeanCode.Contracts.Security;
4 | using LeanCode.CQRS.Security;
5 |
6 | namespace LeanCode.CQRS.AspNetCore.Tests.Integration;
7 |
8 | public interface ICustomAuthorizerParams
9 | {
10 | bool FailAuthorization { get; set; }
11 | }
12 |
13 | [SuppressMessage("?", "CA1040", Justification = "Marker interface")]
14 | public interface ICustomAuthorizer { }
15 |
16 | public class CustomAuthorizer : CustomAuthorizer, ICustomAuthorizer
17 | {
18 | protected override Task CheckIfAuthorizedAsync(
19 | ClaimsPrincipal user,
20 | ICustomAuthorizerParams obj,
21 | CancellationToken cancellationToken
22 | )
23 | {
24 | return Task.FromResult(!obj.FailAuthorization);
25 | }
26 | }
27 |
28 | public sealed class CustomAuthorizeWhenAttribute : AuthorizeWhenAttribute
29 | {
30 | public CustomAuthorizeWhenAttribute()
31 | : base(typeof(ICustomAuthorizer)) { }
32 | }
33 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests.Integration/HttpContextCustomAuthorizer.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using LeanCode.Contracts.Security;
3 | using LeanCode.CQRS.Security;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace LeanCode.CQRS.AspNetCore.Tests.Integration;
7 |
8 | public interface IHttpContextCustomAuthorizerParams
9 | {
10 | bool FailAuthorization { get; set; }
11 | }
12 |
13 | [SuppressMessage("?", "CA1040", Justification = "Marker interface")]
14 | public interface IHttpContextCustomAuthorizer { }
15 |
16 | public class HttpContextCustomAuthorizer
17 | : HttpContextCustomAuthorizer,
18 | IHttpContextCustomAuthorizer
19 | {
20 | protected override Task CheckIfAuthorizedAsync(HttpContext context, IHttpContextCustomAuthorizerParams obj)
21 | {
22 | return Task.FromResult(!obj.FailAuthorization);
23 | }
24 | }
25 |
26 | public sealed class HttpContextCustomAuthorizeWhenAttribute : AuthorizeWhenAttribute
27 | {
28 | public HttpContextCustomAuthorizeWhenAttribute()
29 | : base(typeof(IHttpContextCustomAuthorizer)) { }
30 | }
31 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests.Integration/LeanCode.CQRS.AspNetCore.Tests.Integration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | enable
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests.Integration/TestOperation.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Tests.Integration;
6 |
7 | [HttpContextCustomAuthorizeWhen]
8 | public class TestOperation : IOperation, IHttpContextCustomAuthorizerParams
9 | {
10 | public bool FailAuthorization { get; set; }
11 |
12 | public int X { get; set; }
13 | public int Y { get; set; }
14 | }
15 |
16 | public class TestOperationResult
17 | {
18 | public int Sum { get; set; }
19 | }
20 |
21 | public class TestOperationHandler : IOperationHandler
22 | {
23 | public Task ExecuteAsync(HttpContext context, TestOperation operation)
24 | {
25 | return Task.FromResult(new TestOperationResult { Sum = operation.X + operation.Y });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests.Integration/TestQuery.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Tests.Integration;
6 |
7 | [CustomAuthorizeWhen]
8 | public class TestQuery : IQuery, ICustomAuthorizerParams
9 | {
10 | public bool FailAuthorization { get; set; }
11 |
12 | public int X { get; set; }
13 | public int Y { get; set; }
14 | }
15 |
16 | public class TestQueryResult
17 | {
18 | public int Sum { get; set; }
19 | }
20 |
21 | public class TestQueryHandler : IQueryHandler
22 | {
23 | public Task ExecuteAsync(HttpContext context, TestQuery query)
24 | {
25 | return Task.FromResult(new TestQueryResult { Sum = query.X + query.Y });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/LeanCode.CQRS.AspNetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | enable
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Local/Context/LocalCallServiceProvidersFeatureTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using LeanCode.CQRS.AspNetCore.Local.Context;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Xunit;
5 |
6 | namespace LeanCode.CQRS.AspNetCore.Tests.Local.Context;
7 |
8 | public class LocalCallServiceProvidersFeatureTests
9 | {
10 | private readonly IServiceProvider serviceProvider = new ServiceCollection().BuildServiceProvider();
11 |
12 | [Fact]
13 | public void RequestServices_can_be_changed()
14 | {
15 | var feature = new LocalCallServiceProvidersFeature(serviceProvider);
16 | feature.RequestServices.Should().BeSameAs(serviceProvider);
17 |
18 | feature.RequestServices = new ServiceCollection().BuildServiceProvider();
19 | feature.RequestServices.Should().NotBeSameAs(serviceProvider);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Local/Context/NullEndpointFeatureTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using LeanCode.CQRS.AspNetCore.Local.Context;
3 | using Xunit;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Tests.Local.Context;
6 |
7 | public class NullEndpointFeatureTests
8 | {
9 | [Fact]
10 | public void Endpoint_always_returns_null()
11 | {
12 | NullEndpointFeature.Empty.Endpoint.Should().BeNull();
13 | NullEndpointFeature.Empty.Endpoint = new(null, null, null);
14 | NullEndpointFeature.Empty.Endpoint.Should().BeNull();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Local/Context/NullRequestCookieCollectionTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using LeanCode.CQRS.AspNetCore.Local.Context;
3 | using Xunit;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Tests.Local.Context;
6 |
7 | public class NullRequestCookieCollectionTests
8 | {
9 | [Fact]
10 | public void Count_should_be_zero()
11 | {
12 | NullRequestCookieCollection.Empty.Count.Should().Be(0);
13 | }
14 |
15 | [Fact]
16 | public void ContainsKey_should_always_return_false()
17 | {
18 | NullRequestCookieCollection.Empty.ContainsKey("").Should().BeFalse();
19 | }
20 |
21 | [Fact]
22 | public void GetEnumerator_should_return_empty_enumerable()
23 | {
24 | using var enumerator = NullRequestCookieCollection.Empty.GetEnumerator();
25 | enumerator.MoveNext().Should().BeFalse();
26 | }
27 |
28 | [Fact]
29 | public void TryGetValue_should_always_return_false_and_null()
30 | {
31 | NullRequestCookieCollection.Empty.TryGetValue("", out var value).Should().BeFalse();
32 | value.Should().BeNull();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Local/Context/NullResponseCookiesTests.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.AspNetCore.Local.Context;
2 | using Xunit;
3 |
4 | namespace LeanCode.CQRS.AspNetCore.Tests.Local.Context;
5 |
6 | public class NullResponseCookiesTests
7 | {
8 | [Fact]
9 | [System.Diagnostics.CodeAnalysis.SuppressMessage(
10 | "Security",
11 | "CA5382:Use Secure Cookies In ASP.NET Core",
12 | Justification = "Tests."
13 | )]
14 | public void Append_should_not_throw()
15 | {
16 | NullResponseCookies.Empty.Append("", "");
17 | }
18 |
19 | [Fact]
20 | public void Append_with_options_should_not_throw()
21 | {
22 | NullResponseCookies.Empty.Append("", "", new());
23 | }
24 |
25 | [Fact]
26 | public void Delete_should_not_throw()
27 | {
28 | NullResponseCookies.Empty.Delete("");
29 | }
30 |
31 | [Fact]
32 | public void Delete_with_options_should_not_throw()
33 | {
34 | NullResponseCookies.Empty.Delete("", new());
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Local/Context/NullWebSocketManagerTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using LeanCode.CQRS.AspNetCore.Local.Context;
3 | using Xunit;
4 |
5 | namespace LeanCode.CQRS.AspNetCore.Tests.Local.Context;
6 |
7 | public class NullWebSocketManagerTests
8 | {
9 | [Fact]
10 | public void IsWebSocketRequest_should_always_be_false()
11 | {
12 | NullWebSocketManager.Empty.IsWebSocketRequest.Should().BeFalse();
13 | }
14 |
15 | [Fact]
16 | public void Request_protocols_should_be_empty_and_readonly()
17 | {
18 | NullWebSocketManager.Empty.WebSocketRequestedProtocols.Should().BeEmpty();
19 | NullWebSocketManager.Empty.WebSocketRequestedProtocols.IsReadOnly.Should().BeTrue();
20 |
21 | var act = () => NullWebSocketManager.Empty.WebSocketRequestedProtocols.Add("");
22 | act.Should().Throw();
23 | }
24 |
25 | [Fact]
26 | public async Task Accept_should_always_throw()
27 | {
28 | var act = () => NullWebSocketManager.Empty.AcceptWebSocketAsync((string?)null);
29 | await act.Should().ThrowAsync();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.MassTransitRelay.Tests/Integration/HandledLog.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CQRS.MassTransitRelay.Tests.Integration;
2 |
3 | ///
4 | /// The purpose here is rather to verify if entities are saved in the database along with published messages.
5 | ///
6 | public class HandledLog
7 | {
8 | public Guid CorrelationId { get; private set; }
9 | public string HandlerName { get; private set; }
10 |
11 | private HandledLog(Guid correlationId, string handlerName)
12 | {
13 | CorrelationId = correlationId;
14 | HandlerName = handlerName;
15 | }
16 |
17 | public static void Report(TestDbContext dbContext, Guid correlationId, string handlerName)
18 | {
19 | dbContext.HandledLog.Add(new(correlationId, handlerName));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.MassTransitRelay.Tests/Integration/TestCommand.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.Contracts.Security;
3 | using LeanCode.CQRS.Execution;
4 | using LeanCode.DomainModels.Model;
5 | using Microsoft.AspNetCore.Http;
6 |
7 | namespace LeanCode.CQRS.MassTransitRelay.Tests.Integration;
8 |
9 | [AllowUnauthorized]
10 | public class TestCommand : ICommand
11 | {
12 | public Guid CorrelationId { get; set; }
13 | }
14 |
15 | public class TestCommandHandler : ICommandHandler
16 | {
17 | private readonly TestDbContext dbContext;
18 |
19 | public TestCommandHandler(TestDbContext dbContext)
20 | {
21 | this.dbContext = dbContext;
22 | }
23 |
24 | public Task ExecuteAsync(HttpContext context, TestCommand command)
25 | {
26 | DomainEvents.Raise(new Event1(command.CorrelationId));
27 | HandledLog.Report(dbContext, command.CorrelationId, nameof(TestCommandHandler));
28 | return Task.CompletedTask;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.MassTransitRelay.Tests/Integration/TestDbContext.cs:
--------------------------------------------------------------------------------
1 | using MassTransit;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace LeanCode.CQRS.MassTransitRelay.Tests.Integration;
5 |
6 | public class TestDbContext : DbContext
7 | {
8 | public DbSet HandledLog => Set();
9 |
10 | public TestDbContext(DbContextOptions opts)
11 | : base(opts) { }
12 |
13 | protected override void OnModelCreating(ModelBuilder modelBuilder)
14 | {
15 | modelBuilder.Entity(cfg => cfg.HasKey(e => new { e.CorrelationId, e.HandlerName }));
16 |
17 | modelBuilder.AddInboxStateEntity();
18 | modelBuilder.AddOutboxMessageEntity();
19 | modelBuilder.AddOutboxStateEntity();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.MassTransitRelay.Tests/LeanCode.CQRS.MassTransitRelay.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.RemoteHttp.Client.Tests/LeanCode.CQRS.RemoteHttp.Client.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | enable
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.Security.Tests/LeanCode.CQRS.Security.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/CQRS/LeanCode.CQRS.Validation.Fluent.Tests/LeanCode.CQRS.Validation.Fluent.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Core/LeanCode.Components.Startup.Tests/LeanCode.Components.Startup.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | LeanCode.Startup.Autofac.Tests
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/Domain/LeanCode.DomainModels.EF.Tests/LeanCode.DomainModels.EF.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/Domain/LeanCode.DomainModels.EF.Tests/ModelConfigurationBuilderWrapper.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Internal;
4 | using Microsoft.Extensions.DependencyInjection;
5 |
6 | namespace LeanCode.DomainModels.EF.Tests;
7 |
8 | [SuppressMessage("?", "EF1001", Justification = "Tests.")]
9 | internal sealed class ModelConfigurationBuilderWrapper : ModelConfigurationBuilder
10 | {
11 | public ModelConfigurationBuilderWrapper()
12 | : base(new(), new ServiceCollection().BuildServiceProvider()) { }
13 |
14 | public ModelConfiguration Build() => ModelConfiguration;
15 | }
16 |
--------------------------------------------------------------------------------
/test/Domain/LeanCode.DomainModels.EF.Tests/TypedIds.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Ids;
2 |
3 | namespace LeanCode.DomainModels.EF.Tests;
4 |
5 | [TypedId(TypedIdFormat.RawInt)]
6 | public readonly partial record struct IntId;
7 |
8 | [TypedId(TypedIdFormat.RawLong)]
9 | public readonly partial record struct LongId;
10 |
11 | [TypedId(TypedIdFormat.RawGuid)]
12 | public readonly partial record struct GuidId;
13 |
14 | [TypedId(TypedIdFormat.PrefixedGuid)]
15 | public readonly partial record struct PrefixedGuidId;
16 |
--------------------------------------------------------------------------------
/test/Domain/LeanCode.DomainModels.EventsExecution.TestHelpers.Tests/Events.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Model;
2 | using LeanCode.TimeProvider;
3 |
4 | namespace LeanCode.DomainModels.EventsExecution.TestHelpers.Tests;
5 |
6 | internal sealed class SampleEvent1 : IDomainEvent
7 | {
8 | public Guid Id { get; }
9 | public DateTime DateOccurred { get; }
10 |
11 | public SampleEvent1(Guid id)
12 | {
13 | Id = id;
14 | DateOccurred = Time.UtcNow;
15 | }
16 | }
17 |
18 | internal sealed class SampleEvent2 : IDomainEvent
19 | {
20 | public Guid Id { get; }
21 | public DateTime DateOccurred { get; }
22 |
23 | public SampleEvent2(Guid id)
24 | {
25 | Id = id;
26 | DateOccurred = Time.UtcNow;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test/Domain/LeanCode.DomainModels.EventsExecution.TestHelpers.Tests/LeanCode.DomainModels.EventsExecution.TestHelpers.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/Domain/LeanCode.TimeProvider.Tests/LeanCode.TimeProvider.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/Helpers/LeanCode.UserIdExtractors.Tests/LeanCode.UserIdExtractors.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.AuditLogs.Tests/AuditLogsMiddlewareTests.cs:
--------------------------------------------------------------------------------
1 | using MassTransit;
2 | using Microsoft.AspNetCore.Http;
3 | using NSubstitute;
4 | using Xunit;
5 |
6 | namespace LeanCode.AuditLogs.Tests;
7 |
8 | public sealed class AuditLogsMiddlewareTests
9 | {
10 | [Fact]
11 | public async Task Extracts_changes_after_pipeline_execution()
12 | {
13 | const string RequestPath = "/test.request.path";
14 | using var dbContext = new TestDbContext();
15 | var bus = Substitute.For();
16 | var publisher = Substitute.For();
17 | var middleware = new AuditLogsMiddleware(_ => Task.CompletedTask);
18 | var httpContext = Substitute.For();
19 | httpContext.Request.Path.Returns(PathString.FromUriComponent(new Uri(RequestPath)));
20 |
21 | await middleware.InvokeAsync(httpContext, dbContext, bus, publisher);
22 |
23 | await publisher.Received().ExtractAndPublishAsync(dbContext, bus, RequestPath, Arg.Any());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.AuditLogs.Tests/LeanCode.AuditLogs.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | enable
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Azure.Tests/LeanCode.Azure.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.AzureIdentity.Tests/AzureIdentityFact.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Test.Helpers;
2 |
3 | namespace LeanCode.AzureIdentity.Tests;
4 |
5 | public sealed class AzureIdentityFact : ExternalServiceFactAttribute
6 | {
7 | protected override string ServiceType => "azure-identity";
8 | protected override IReadOnlyCollection RequiredEnvVariables { get; }
9 |
10 | public AzureIdentityFact(params string[] requiredEnvVariables)
11 | {
12 | RequiredEnvVariables = requiredEnvVariables.ToHashSet();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.AzureIdentity.Tests/LeanCode.AzureIdentity.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | enable
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.AzureIdentity.Tests/Properties.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | // This is necessary, because some tests rely on setting environmental variables, which might be overwritten by tests.
4 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)]
5 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Dapper.Tests/LeanCode.Dapper.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Firebase.FCM.Tests/FCMFact.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Test.Helpers;
2 |
3 | namespace LeanCode.Firebase.FCM.Tests;
4 |
5 | public sealed class FCMFactAttribute : ExternalServiceFactAttribute
6 | {
7 | protected override string ServiceType => "fcm";
8 | protected override IReadOnlyCollection RequiredEnvVariables { get; } = ["FCM_KEY", "FCM_TOKEN"];
9 | }
10 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Firebase.FCM.Tests/LeanCode.Firebase.FCM.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | enable
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ForceUpdate.Tests/LeanCode.ForceUpdate.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | enable
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Kratos.Tests/LeanCode.Kratos.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Localization.Tests/LeanCode.Localization.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Localization.Tests/ResourceManagerStringLocalizerTests.pl.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Zamówienie
5 |
6 |
7 | Zamówienie nr {0}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Localization.Tests/ResourceManagerStringLocalizerTests.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Order
5 |
6 |
7 | Order no. {0}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Logging.AspNetCore.Tests/LeanCode.Logging.AspNetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Logging.Tests/LeanCode.Logging.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Mixpanel.Tests/LeanCode.Mixpanel.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.SendGrid.Tests/LeanCode.SendGrid.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.Serialization.Tests/LeanCode.Serialization.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.SmsSender.Tests/LeanCode.SmsSender.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/LeanCode.ViewRenderer.Razor.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | PreserveNewest
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/A/ViewA.cshtml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/A/ViewA.cshtml
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/A/ViewB.cshtml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/A/ViewB.cshtml
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/A/ViewC.cshtml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/A/ViewC.cshtml
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/B/ViewA.cshtml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/B/ViewA.cshtml
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/B/ViewB.cstxt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/B/ViewB.cstxt
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/B/ViewD.cshtml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancodepl/corelibrary/12bbe619e6230cd16715d58e1f5210ede9efb72e/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/B/ViewD.cshtml
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Cache/Simple.cshtml:
--------------------------------------------------------------------------------
1 | this is simple view
2 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Compiler/CompiledError.cshtml:
--------------------------------------------------------------------------------
1 | @NonExistingField
2 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Compiler/Dotted.Name.cshtml:
--------------------------------------------------------------------------------
1 | Dotted name
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Compiler/Layouted.cshtml:
--------------------------------------------------------------------------------
1 | @layout Simple
2 | this is simple view
3 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Compiler/LayoutedSecondLine.cshtml:
--------------------------------------------------------------------------------
1 | Test
2 | @layout Simple
3 | this is simple view
4 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Compiler/RazorError.cshtml:
--------------------------------------------------------------------------------
1 | @if () { wont }
2 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Compiler/Simple.cshtml:
--------------------------------------------------------------------------------
1 | this is simple view
2 |
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/Functions.cshtml:
--------------------------------------------------------------------------------
1 | @functions {
2 | string Hello()
3 | {
4 | return "Hello";
5 | }
6 | }
7 | @Hello()
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/HierarchicalLayout.cshtml:
--------------------------------------------------------------------------------
1 | @layout HierarchicalLayout1
2 | CONTENT
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/HierarchicalLayout1.cshtml:
--------------------------------------------------------------------------------
1 | @layout HierarchicalLayout2
2 | HL1-PRE
3 | @await RenderBodyAsync()
4 | HL1-POST
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/HierarchicalLayout2.cshtml:
--------------------------------------------------------------------------------
1 | HL2-PRE
2 | @await RenderBodyAsync()
3 | HL2-POST
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/Layout1.cshtml:
--------------------------------------------------------------------------------
1 | LAYOUT-PRE
2 | @await RenderBodyAsync()
3 | LAYOUT-POST
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/LayoutModel.cshtml:
--------------------------------------------------------------------------------
1 | @layout LayoutModel1
2 | @Model.Child
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/LayoutModel1.cshtml:
--------------------------------------------------------------------------------
1 | PRE-@Model.Layout
2 | @await RenderBodyAsync()
3 | POST-@Model.Layout
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/Layouted.cshtml:
--------------------------------------------------------------------------------
1 | @layout Layout1
2 | CONTENT
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/Model.cshtml:
--------------------------------------------------------------------------------
1 | @Model.Child
--------------------------------------------------------------------------------
/test/Infrastructure/LeanCode.ViewRenderer.Razor.Tests/Views/Renderer/Simple.cshtml:
--------------------------------------------------------------------------------
1 | SIMPLE
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/App/AppRoles.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CQRS.Security;
2 |
3 | namespace LeanCode.IntegrationTests.App;
4 |
5 | public class AppRoles : IRoleRegistration
6 | {
7 | public IEnumerable Roles { get; } = [new Role("user", "user")];
8 | }
9 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/App/AuthConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using LeanCode.IntegrationTestHelpers;
3 |
4 | namespace LeanCode.IntegrationTests.App;
5 |
6 | public static class AuthConfig
7 | {
8 | public static readonly Guid UserId = Guid.NewGuid();
9 |
10 | public static readonly ClaimsPrincipal User = new(
11 | new ClaimsIdentity(
12 | new Claim[] { new("sub", UserId.ToString()), new("role", "user") },
13 | TestAuthenticationHandler.SchemeName,
14 | "sub",
15 | "role"
16 | )
17 | );
18 |
19 | public static readonly ClaimsPrincipal UserWithoutRole = new(
20 | new ClaimsIdentity(
21 | new Claim[] { new("sub", UserId.ToString()) },
22 | TestAuthenticationHandler.SchemeName,
23 | "sub",
24 | "role"
25 | )
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/App/Entity.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using LeanCode.DomainModels.Model;
3 | using LeanCode.TimeProvider;
4 |
5 | namespace LeanCode.IntegrationTests.App;
6 |
7 | public class Entity : IAggregateRootWithoutOptimisticConcurrency
8 | {
9 | public Guid Id { get; set; }
10 | public string Value { get; set; } = null!;
11 | }
12 |
13 | public class EntityAdded : IDomainEvent
14 | {
15 | public Guid Id { get; private init; }
16 | public DateTime DateOccurred { get; private init; }
17 |
18 | public string Value { get; private init; }
19 | public Guid EntityId { get; private init; }
20 |
21 | [JsonConstructor]
22 | public EntityAdded(Guid id, DateTime dateOccurred, string value, Guid entityId)
23 | {
24 | Id = id;
25 | DateOccurred = dateOccurred;
26 | Value = value;
27 | EntityId = entityId;
28 | }
29 |
30 | public EntityAdded(Entity entity)
31 | {
32 | Id = Guid.NewGuid();
33 | DateOccurred = Time.UtcNow;
34 |
35 | EntityId = entity.Id;
36 | Value = entity.Value;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/App/Meeting.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.DomainModels.Model;
2 |
3 | namespace LeanCode.IntegrationTests.App;
4 |
5 | public class Meeting
6 | {
7 | public Guid Id { get; set; }
8 | public string Name { get; set; } = default!;
9 | public TimestampTz StartTime { get; set; } = default!;
10 | }
11 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/App/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using LeanCode.AzureIdentity;
3 | using LeanCode.Logging.AspNetCore;
4 | using LeanCode.Startup.MicrosoftDI;
5 | using Microsoft.Extensions.Hosting;
6 |
7 | namespace LeanCode.IntegrationTests.App;
8 |
9 | public static class Program
10 | {
11 | [SuppressMessage("?", "IDE0060", Justification = "`args` are required by convention.")]
12 | public static IHostBuilder CreateHostBuilder(string[] args)
13 | {
14 | return LeanProgram
15 | .BuildMinimalHost()
16 | .AddAppConfigurationFromAzureKeyVaultOnNonDevelopmentEnvironment()
17 | .ConfigureDefaultLogging(projectName: "test", destructurers: new[] { typeof(Program).Assembly });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/PostgresOnlyFactAttribute.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Test.Helpers;
2 |
3 | namespace LeanCode.IntegrationTests;
4 |
5 | public sealed class PostgresOnlyFactAttribute : IntegrationFactAttribute
6 | {
7 | public override IReadOnlyCollection> GetTraits() =>
8 | base.GetTraits().Append(new("database", "postgres")).ToList();
9 | }
10 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/docker/.env:
--------------------------------------------------------------------------------
1 | COMPOSE_PROJECT_NAME=corelib-integration-tests
2 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/sdk:9.0
2 |
3 | WORKDIR /app/code/test/LeanCode.IntegrationTests
4 | ENTRYPOINT dotnet run -- --explicit on
5 |
--------------------------------------------------------------------------------
/test/LeanCode.IntegrationTests/docker/Dockerfile.watch:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/sdk:9.0
2 |
3 | # Wait for SQL Server first
4 | WORKDIR /app/code/test/LeanCode.IntegrationTests
5 | ENTRYPOINT dotnet restore && dotnet watch test -- --explicit on
6 |
--------------------------------------------------------------------------------
/test/LeanCode.Test.Helpers/IntegrationFactAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Xunit;
3 | using Xunit.v3;
4 |
5 | namespace LeanCode.Test.Helpers;
6 |
7 | [SuppressMessage("?", "CA1813", Justification = "The attribute is inherited by other attributes and cannot be sealed.")]
8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
9 | public class IntegrationFactAttribute : FactAttribute, ITraitAttribute
10 | {
11 | public IntegrationFactAttribute()
12 | {
13 | Explicit = true;
14 | }
15 |
16 | public virtual IReadOnlyCollection> GetTraits() =>
17 | [new("category", "integration"), new("integration", "true")];
18 | }
19 |
--------------------------------------------------------------------------------
/test/LeanCode.Test.Helpers/LeanCode.Test.Helpers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | Library
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/LeanCode.Test.Helpers/LongRunningFact.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 | using Xunit.v3;
3 |
4 | namespace LeanCode.Test.Helpers;
5 |
6 | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
7 | public sealed class LongRunningFact : FactAttribute, ITraitAttribute
8 | {
9 | public IReadOnlyCollection> GetTraits() => [new("category", "long-running")];
10 | }
11 |
12 | public class LongRunningFactTest
13 | {
14 | [LongRunningFact]
15 | public void Long_running_test() { }
16 | }
17 |
--------------------------------------------------------------------------------
/test/LeanCode.TestCoverage.Tests/LeanCode.TestCoverage.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/LeanCode.TestCoverage.Tests/TestCoverageTests.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.TestCoverage.Tests;
2 |
3 | public class TestCoverageTests
4 | {
5 | [Xunit.Fact]
6 | public void Include_all_source_projects_for_test_coverage() { }
7 | }
8 |
--------------------------------------------------------------------------------
/test/Testing/LeanCode.IntegrationTestHelpers.Tests/App/AuthQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using LeanCode.Contracts;
3 | using LeanCode.CQRS.Execution;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace LeanCode.IntegrationTestHelpers.Tests.App;
7 |
8 | public class AuthQuery : IQuery { }
9 |
10 | public class AuthResult
11 | {
12 | public bool IsAuthenticated { get; set; }
13 |
14 | [SuppressMessage("?", "CA2227", Justification = "Public modifiable list as part of the contract")]
15 | public List> Claims { get; set; } = default!;
16 | }
17 |
18 | public class AuthQueryHandler : IQueryHandler
19 | {
20 | public Task ExecuteAsync(HttpContext context, AuthQuery query)
21 | {
22 | var principal = context.User;
23 |
24 | var result = new AuthResult
25 | {
26 | IsAuthenticated = principal.Identity?.IsAuthenticated ?? false,
27 | Claims = principal.Claims.Select(c => KeyValuePair.Create(c.Type, c.Value)).ToList(),
28 | };
29 |
30 | return Task.FromResult(result);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/Testing/LeanCode.IntegrationTestHelpers.Tests/App/Command.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.IntegrationTestHelpers.Tests.App;
6 |
7 | public class Command : ICommand
8 | {
9 | public int Id { get; set; }
10 | public string? Data { get; set; }
11 | }
12 |
13 | public class CommandCH : ICommandHandler
14 | {
15 | private readonly TestDbContext dbContext;
16 |
17 | public CommandCH(TestDbContext dbContext)
18 | {
19 | this.dbContext = dbContext;
20 | }
21 |
22 | public Task ExecuteAsync(HttpContext context, Command command)
23 | {
24 | dbContext.Entities.Add(new Entity { Id = command.Id, Data = command.Data });
25 | return dbContext.SaveChangesAsync();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/Testing/LeanCode.IntegrationTestHelpers.Tests/App/ConnectionKeeper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Hosting;
4 |
5 | namespace LeanCode.IntegrationTestHelpers.Tests.App;
6 |
7 | public class ConnectionKeeper : IHostedService
8 | {
9 | private readonly AsyncServiceScope scope;
10 | private readonly TestDbContext dbContext;
11 |
12 | public ConnectionKeeper(IServiceProvider serviceProvider)
13 | {
14 | scope = serviceProvider.CreateAsyncScope();
15 |
16 | dbContext = scope.ServiceProvider.GetRequiredService();
17 | }
18 |
19 | public Task StartAsync(CancellationToken cancellationToken) =>
20 | dbContext.Database.OpenConnectionAsync(cancellationToken);
21 |
22 | public async Task StopAsync(CancellationToken cancellationToken)
23 | {
24 | await dbContext.Database.CloseConnectionAsync();
25 | await scope.DisposeAsync();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/Testing/LeanCode.IntegrationTestHelpers.Tests/App/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using LeanCode.AzureIdentity;
3 | using LeanCode.Logging.AspNetCore;
4 | using LeanCode.Startup.MicrosoftDI;
5 | using Microsoft.Extensions.Hosting;
6 |
7 | namespace LeanCode.IntegrationTestHelpers.Tests.App;
8 |
9 | public static class Program
10 | {
11 | [SuppressMessage("?", "IDE0060", Justification = "`args` are required by convention.")]
12 | public static IHostBuilder CreateHostBuilder(string[] args)
13 | {
14 | return LeanProgram
15 | .BuildMinimalHost()
16 | .AddAppConfigurationFromAzureKeyVaultOnNonDevelopmentEnvironment()
17 | .ConfigureDefaultLogging(
18 | projectName: "integration-tests",
19 | destructurers: new[] { typeof(Program).Assembly }
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/Testing/LeanCode.IntegrationTestHelpers.Tests/App/Query.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace LeanCode.IntegrationTestHelpers.Tests.App;
7 |
8 | public class Query : IQuery
9 | {
10 | public int Id { get; set; }
11 | }
12 |
13 | public class QueryQH : IQueryHandler
14 | {
15 | private readonly TestDbContext dbContext;
16 |
17 | public QueryQH(TestDbContext dbContext)
18 | {
19 | this.dbContext = dbContext;
20 | }
21 |
22 | public Task ExecuteAsync(HttpContext context, Query query)
23 | {
24 | return dbContext.Entities.Where(e => e.Id == query.Id).Select(e => e.Data).FirstOrDefaultAsync();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/Testing/LeanCode.IntegrationTestHelpers.Tests/App/TestDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace LeanCode.IntegrationTestHelpers.Tests.App;
4 |
5 | public class Entity
6 | {
7 | public int Id { get; set; }
8 | public string? Data { get; set; }
9 | }
10 |
11 | public class TestDbContext : DbContext
12 | {
13 | public DbSet Entities => Set();
14 |
15 | public TestDbContext(DbContextOptions opts)
16 | : base(opts) { }
17 |
18 | protected override void OnModelCreating(ModelBuilder modelBuilder)
19 | {
20 | modelBuilder.Entity(cfg =>
21 | {
22 | cfg.HasKey(e => e.Id);
23 | cfg.Property(e => e.Id).ValueGeneratedNever();
24 | cfg.Property(e => e.Data);
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/Analyzers/SuggestCommandsHaveValidatorsTests.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CodeAnalysis.Analyzers;
2 | using LeanCode.CodeAnalysis.Tests.Verifiers;
3 | using Microsoft.CodeAnalysis.Diagnostics;
4 | using Xunit;
5 |
6 | namespace LeanCode.CodeAnalysis.Tests.Analyzers;
7 |
8 | public class SuggestCommandsHaveValidatorsTests : DiagnosticVerifier
9 | {
10 | [Fact]
11 | public async Task Ignores_commands_with_validators_reports_not_validated()
12 | {
13 | var source = await File.ReadAllTextAsync("TestSamples/Command_validation.cs");
14 | var diag = new DiagnosticResult(DiagnosticsIds.CommandsShouldHaveValidators, 18, 13);
15 | await VerifyDiagnostics(source, diag);
16 | }
17 |
18 | protected override DiagnosticAnalyzer GetDiagnosticAnalyzer()
19 | {
20 | return new SuggestCommandsHaveValidators();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/LeanCode.CodeAnalysis.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreserveNewest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/CQRS/Command_handlers.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.CQRS;
6 |
7 | public class FirstCommandCH : ICommandHandler
8 | {
9 | public Task ExecuteAsync(HttpContext context, FirstCommand command) => throw new NotImplementedException();
10 | }
11 |
12 | public class MultipleCommandsCH : ICommandHandler, ICommandHandler
13 | {
14 | public Task ExecuteAsync(HttpContext context, FirstCommand command) => throw new NotImplementedException();
15 |
16 | public Task ExecuteAsync(HttpContext context, SecondCommand command) => throw new NotImplementedException();
17 | }
18 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/CQRS/Operation_handlers.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.CQRS;
6 |
7 | public class FirstOperationOH : IOperationHandler
8 | {
9 | public Task ExecuteAsync(HttpContext context, FirstOperation operation) =>
10 | throw new NotImplementedException();
11 | }
12 |
13 | public class MultipleOperationsOH : IOperationHandler, IOperationHandler
14 | {
15 | public Task ExecuteAsync(HttpContext context, FirstOperation operation) =>
16 | throw new NotImplementedException();
17 |
18 | public Task ExecuteAsync(HttpContext context, SecondOperation operation) =>
19 | throw new NotImplementedException();
20 | }
21 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/CQRS/Query_handlers.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.CQRS;
6 |
7 | public class FirstQueryQH : IQueryHandler
8 | {
9 | public Task ExecuteAsync(HttpContext context, FirstQuery query) => throw new NotImplementedException();
10 | }
11 |
12 | public class MultipleQueriesQH : IQueryHandler, IQueryHandler
13 | {
14 | public Task ExecuteAsync(HttpContext context, FirstQuery query) => throw new NotImplementedException();
15 |
16 | public Task ExecuteAsync(HttpContext context, SecondQuery query) => throw new NotImplementedException();
17 | }
18 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/CQRS/Validators.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using LeanCode.Contracts;
3 |
4 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.CQRS;
5 |
6 | // Roslyn has only context of this file in tests so it's necessary to declare `AcceptedValidatorCommand` here
7 | // to check if it implements `ICommand` interface.
8 | public class ValidatorCommand : ICommand { }
9 |
10 | public class ValidatorOperation : IOperation { }
11 |
12 | public class ValidatorCommandCV : AbstractValidator { }
13 |
14 | // Do not raise diagnostics for classes not implementing `ICommand` interface.
15 | public class Validator : AbstractValidator { }
16 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/Contracts/Commands.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.Contracts.Security;
3 |
4 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
5 |
6 | public abstract class BaseCommand : ICommand { }
7 |
8 | [AuthorizeWhenHasAnyOf("Some_permission")]
9 | public class FirstCommand : BaseCommand { }
10 |
11 | public class SecondCommand : FirstCommand { }
12 |
13 | [AuthorizeWhenCustom]
14 | public class ThirdCommand : ICommand { }
15 |
16 | [AllowUnauthorized]
17 | public class UnauthorizedCommand : ICommand { }
18 |
19 | public sealed class AuthorizeWhenCustomAttribute : AuthorizeWhenAttribute
20 | {
21 | public AuthorizeWhenCustomAttribute(Type authorizerType = null)
22 | : base(authorizerType ?? typeof(object)) { }
23 | }
24 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/Contracts/Operations.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.Contracts;
2 | using LeanCode.Contracts.Security;
3 |
4 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
5 |
6 | public abstract class BaseOperation : IOperation { }
7 |
8 | [AuthorizeWhenHasAnyOf("Sample_permission")]
9 | public class FirstOperation : BaseOperation { }
10 |
11 | public class SecondOperation : FirstOperation { }
12 |
13 | [AuthorizeWhenHasAnyOf("Sample_permission")]
14 | public class ThirdOperation : IOperation { }
15 |
16 | [AllowUnauthorized]
17 | public class UnauthorizedOperation : IOperation { }
18 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/Contracts/Queries.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using LeanCode.Contracts;
3 | using LeanCode.Contracts.Security;
4 |
5 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
6 |
7 | public abstract class BaseQuery : IQuery { }
8 |
9 | [AuthorizeWhenHasAnyOf("Sample_permission")]
10 | public class FirstQuery : BaseQuery { }
11 |
12 | public class SecondQuery : FirstQuery { }
13 |
14 | [AuthorizeWhenHasAnyOf("Sample_permission")]
15 | public class ThirdQuery : IQuery { }
16 |
17 | [SuppressMessage("??", "CA1040", Justification = "Empty marker interface")]
18 | public interface ICustomQueryAuthorizer { }
19 |
20 | public sealed class CustomQueryAuthorizerAttribute : AuthorizeWhenAttribute { }
21 |
22 | [CustomQueryAuthorizer]
23 | public class CustomAuthorizedQuery : IQuery { }
24 |
25 | [AllowUnauthorized]
26 | public class UnauthorizedQuery : IQuery { }
27 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Accepted/Methods.cs:
--------------------------------------------------------------------------------
1 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Accepted;
2 |
3 | public static class Methods
4 | {
5 | public static CancellationToken CancellationTokenNamingConvention(CancellationToken cancellationToken)
6 | {
7 | return cancellationToken;
8 | }
9 | }
10 |
11 | // Methods with `override` and `new` keywords are accepted as they might come from external source
12 | public class OverrideMethod : Rejected.Methods
13 | {
14 | public override CancellationToken CancellationTokenNamingConvention(CancellationToken ct)
15 | {
16 | return ct;
17 | }
18 | }
19 |
20 | public class AcceptedNewMethod : Rejected.Methods
21 | {
22 | public static new CancellationToken CancellationTokenNamingConvention(CancellationToken ct)
23 | {
24 | return ct;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Command_validation.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using LeanCode.Contracts;
3 | using LeanCode.CQRS.Execution;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace LeanCode.CodeAnalysis.Tests.TestSamples;
7 |
8 | public class ValidatedCommand : ICommand { }
9 |
10 | public class Validator : AbstractValidator { }
11 |
12 | public class ValidatedHandler : ICommandHandler
13 | {
14 | public Task ExecuteAsync(HttpContext context, ValidatedCommand command) => Task.CompletedTask;
15 | }
16 |
17 | public class NotValidatedCommand : ICommand { }
18 |
19 | public class NotValidatedHandler : ICommandHandler
20 | {
21 | public Task ExecuteAsync(HttpContext context, NotValidatedCommand command) => Task.CompletedTask;
22 | }
23 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Rejected/CQRS/Command_handlers.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Rejected.CQRS.WrongNamespace;
6 |
7 | public class WrongCommandHandlerName : ICommandHandler
8 | {
9 | public Task ExecuteAsync(HttpContext context, FirstCommand command) => throw new NotImplementedException();
10 | }
11 |
12 | public class WrongMultipleCommandHandlerName : ICommandHandler, ICommandHandler
13 | {
14 | public Task ExecuteAsync(HttpContext context, FirstCommand command) => throw new NotImplementedException();
15 |
16 | public Task ExecuteAsync(HttpContext context, SecondCommand command) => throw new NotImplementedException();
17 | }
18 |
--------------------------------------------------------------------------------
/test/Tools/LeanCode.CodeAnalysis.Tests/TestSamples/Rejected/CQRS/Operation_handlers.cs:
--------------------------------------------------------------------------------
1 | using LeanCode.CodeAnalysis.Tests.TestSamples.Accepted.Contracts;
2 | using LeanCode.CQRS.Execution;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace LeanCode.CodeAnalysis.Tests.TestSamples.Rejected.CQRS.WrongNamespace;
6 |
7 | public class WrongOperationHandlerName : IOperationHandler
8 | {
9 | public Task