├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── WebInfrastructure.sln ├── WebInfrastructure.sln.DotSettings ├── appveyor.yml ├── build ├── NuGet.config ├── build.cake ├── build.ps1 └── build.sh ├── common.props ├── src ├── Infrastructure │ ├── CQRS.Abstractions │ │ ├── CQRS.Abstractions.csproj │ │ ├── Commands │ │ │ ├── IAsyncCommand.cs │ │ │ ├── ICommand.cs │ │ │ ├── ICommandContext.cs │ │ │ └── ICommandsDispatcher.cs │ │ └── Queries │ │ │ ├── IAsyncCriterion.cs │ │ │ ├── IAsyncQuery.cs │ │ │ ├── ICriterion.cs │ │ │ ├── IQueriesDispatcher.cs │ │ │ ├── IQuery.cs │ │ │ └── QueriesDispatcherExtensions.cs │ ├── CQRS.Implementations │ │ ├── CQRS.Implementations.csproj │ │ ├── Commands │ │ │ ├── CommandsDispatcher.cs │ │ │ └── CommandsFactory │ │ │ │ ├── GenericCommandsFactory.cs │ │ │ │ └── ICommandsFactory.cs │ │ └── Queries │ │ │ ├── QueriesDispatcher.cs │ │ │ └── QueriesFactory │ │ │ ├── GenericQueriesFactory.cs │ │ │ └── IQueriesFactory.cs │ ├── Common │ │ ├── Common.csproj │ │ └── Extensions │ │ │ ├── DictionaryExtensions.cs │ │ │ ├── DoubleExtensions.cs │ │ │ ├── EnumExtensions.cs │ │ │ ├── EnumerableExtensions.cs │ │ │ └── ParallelExtensions.cs │ ├── Dapper │ │ ├── ConnectionsFactory │ │ │ ├── IConnectionsFactory.cs │ │ │ ├── SqlConnectionsFactory.cs │ │ │ └── SqlConnectionsFactoryOptions.cs │ │ ├── Dapper.csproj │ │ ├── Extensions │ │ │ ├── CollectonReader.cs │ │ │ ├── DbConnectionExtensions.cs │ │ │ ├── IColumnsMappingConfigurator.cs │ │ │ ├── IMappingInfoProvider.cs │ │ │ ├── PropertyInfoProviders │ │ │ │ ├── ExpandoObjectMappingInfoProvider.cs │ │ │ │ ├── ManualConfiguredStrictTypeMappingInfoProvider.cs │ │ │ │ └── StrictTypeMappingInfoProvider.cs │ │ │ └── SqlConnectionExtensions.cs │ │ ├── QueryObject.cs │ │ ├── SessionsFactory │ │ │ ├── ISession.cs │ │ │ ├── ISessionsFactory.cs │ │ │ ├── Session.cs │ │ │ ├── SessionExtensions.cs │ │ │ └── SessionsFactory.cs │ │ ├── Tvp │ │ │ ├── IMetaDataOptionsBuilder.cs │ │ │ ├── Int32ValuesList.cs │ │ │ ├── Int64ValuesList.cs │ │ │ ├── MetaDataCreationOptions.cs │ │ │ ├── MetaDataOptionsBuilder.cs │ │ │ ├── PrimitiveValuesList.cs │ │ │ ├── SqlTypesMapper.cs │ │ │ └── TvpParameter.cs │ │ └── TypesHandlers │ │ │ └── CollectionsHandler.cs │ ├── Directory.Build.props │ ├── Queues.Abstractions │ │ ├── Configuration │ │ │ └── ServiceCollectionExtensions.cs │ │ ├── IMessageHandler.cs │ │ ├── MessagesProcessingService.cs │ │ ├── MessagesProcessingServiceOptions.cs │ │ ├── Queues.Abstractions.csproj │ │ └── QueuesFactory │ │ │ ├── Configuration │ │ │ ├── QueueCreationOptions.cs │ │ │ └── QueueCreationOptionsExtensions.cs │ │ │ ├── ExceptionsHandling │ │ │ ├── ErrorInformation.cs │ │ │ ├── ExceptionHandlersFactory.cs │ │ │ ├── ExceptionHandlingPolicy.cs │ │ │ ├── Handlers │ │ │ │ ├── EmptyExceptionHandler.cs │ │ │ │ ├── ErrorsQueuingExceptionHandler.cs │ │ │ │ ├── ExceptionHandlerBase.cs │ │ │ │ └── RequeuingExceptionHandler.cs │ │ │ └── IExceptionHandlersFactory.cs │ │ │ ├── GenericQueuesFactory.cs │ │ │ ├── IGenericQueuesFactory.cs │ │ │ ├── ITypedQueue.cs │ │ │ ├── ITypedQueuesFactory.cs │ │ │ ├── IUntypedQueuesFactory.cs │ │ │ ├── QueueBase.cs │ │ │ └── QueueMessageDescriptionBase.cs │ ├── Queues.RabbitMq │ │ ├── Queues.RabbitMq.csproj │ │ └── QueuesFactory │ │ │ ├── Configuration │ │ │ ├── RabbitCredentianls.cs │ │ │ ├── RabbitQueueCreationOptions.cs │ │ │ ├── ServiceCollectionExtensions.cs │ │ │ └── TypedRabbitQueuesFactoryOptions.cs │ │ │ ├── ExceptionsHandling │ │ │ └── Handlers │ │ │ │ └── RequeuingWithDelayExceptionHandler.cs │ │ │ ├── RabbitMessageDescription.cs │ │ │ ├── RabbitQueue.cs │ │ │ ├── RabbitQueuesFactory.cs │ │ │ └── TypedRabbitQueue.cs │ ├── Web.Authentication │ │ ├── JwtBearer │ │ │ ├── ApplicationBuilderExtensions.cs │ │ │ ├── Configuration │ │ │ │ ├── Issuing │ │ │ │ │ ├── IJwtBearerTokensIssuerBuilder.cs │ │ │ │ │ ├── JwtBearerTokensIssuerBuilder.cs │ │ │ │ │ ├── JwtBearerTokensIssuerBuilderExtensions.cs │ │ │ │ │ ├── ServiceCollectionExtensions.cs │ │ │ │ │ ├── TokenIssueEventHandlerSetup.cs │ │ │ │ │ └── TokensIssuingOptionsExtensions.cs │ │ │ │ └── Validation │ │ │ │ │ ├── JwtBearerOptionsExtensions.cs │ │ │ │ │ └── TokenValidationParametersExtensions.cs │ │ │ ├── ITokenIssueEventHandler.cs │ │ │ ├── Models │ │ │ │ ├── TokenErrorResponseModel.cs │ │ │ │ ├── TokenRequestModel.cs │ │ │ │ └── TokenResponseModel.cs │ │ │ ├── TokensIssuingMiddleware.cs │ │ │ ├── TokensIssuingOptions.cs │ │ │ └── UserClaimsProvider │ │ │ │ ├── Exceptions │ │ │ │ ├── IncorrectPasswordException.cs │ │ │ │ └── LoginNotFoundException.cs │ │ │ │ └── IUserClaimsProvider.cs │ │ └── Web.Authentication.csproj │ ├── Web.Conventions │ │ ├── Responses │ │ │ ├── ApiExceptionResponse.cs │ │ │ ├── ApiResponse.cs │ │ │ ├── ApiResponseError.cs │ │ │ └── ExceptionDescription.cs │ │ └── Web.Conventions.csproj │ ├── Web.Hosting │ │ ├── HostBuilderExtensions.cs │ │ ├── Web.Hosting.csproj │ │ └── WebApiBaseStartup.cs │ ├── Web.Integration │ │ ├── BaseApiClient │ │ │ ├── BaseClient.cs │ │ │ ├── Configuration │ │ │ │ ├── BaseClientOptions.cs │ │ │ │ ├── HttpClientBuilderExtensions.cs │ │ │ │ ├── HttpClientHandlerExtensions.cs │ │ │ │ ├── OptionsBuilderExtensions.cs │ │ │ │ └── ServiceCollectionExtensions.cs │ │ │ ├── Exceptions │ │ │ │ ├── ApiException.cs │ │ │ │ ├── BadRequestException.cs │ │ │ │ ├── NotFoundException.cs │ │ │ │ └── UnauthorizedException.cs │ │ │ └── RequestHeadersExtensions.cs │ │ ├── BaseApiFluentClient │ │ │ ├── BaseFluentClient.cs │ │ │ └── FluentChainedTask.cs │ │ └── Web.Integration.csproj │ ├── Web.Logging.Abstractions │ │ ├── ContextsCapturing │ │ │ ├── CapturedData │ │ │ │ └── CapturedActionExecutingContext.cs │ │ │ └── LoggerExtensions.cs │ │ └── Web.Logging.Abstractions.csproj │ ├── Web.Logging.NLog │ │ ├── ConfigurationBuilderExtensions.cs │ │ ├── LoggingBuilderExtensions.cs │ │ └── Web.Logging.NLog.csproj │ ├── Web.Logging.Serilog │ │ ├── Configuration │ │ │ └── LoggerConfigurationExtensions.cs │ │ ├── Enrichers │ │ │ ├── LogEventHashEnricher.cs │ │ │ ├── LoggerEnrichmentConfigurationExtensions.cs │ │ │ └── MessageTemplateHashEnricher.cs │ │ ├── Formatting │ │ │ └── RenderedJsonFormatter.cs │ │ └── Web.Logging.Serilog.csproj │ ├── Web.Serialization.Abstractions │ │ ├── ISerializer.cs │ │ └── Web.Serialization.Abstractions.csproj │ ├── Web.Serialization.Jil │ │ ├── Configuration │ │ │ ├── MvcBuilderExtensions.cs │ │ │ ├── MvcJilOptions.cs │ │ │ ├── MvcOptionsSetup.cs │ │ │ └── OptionsExtensions.cs │ │ ├── Formatters │ │ │ ├── JilInputFormatter.cs │ │ │ ├── JilOutputFormatter.cs │ │ │ └── MediaTypeHeaderValues.cs │ │ ├── Serializer │ │ │ └── JilSerializer.cs │ │ └── Web.Serialization.Jil.csproj │ ├── Web.Serialization.JsonNet │ │ ├── Configuration │ │ │ ├── JsonSerializerSettingsExtensions.cs │ │ │ └── MvcBuilderExtensions.cs │ │ ├── JsonConverters │ │ │ └── DateTimeConverter.cs │ │ ├── PropertyNamesResolvers │ │ │ └── SnakeCasePropertyNamesContractResolver.cs │ │ ├── Serializer │ │ │ └── JsonNetSerializer.cs │ │ └── Web.Serialization.JsonNet.csproj │ ├── Web.Serialization.Protobuf │ │ ├── Configuration │ │ │ ├── MvcBuilderExtensions.cs │ │ │ ├── MvcOptionsSetup.cs │ │ │ ├── MvcProtobufOptions.cs │ │ │ └── RuntimeTypeModelExtensions.cs │ │ ├── Formatters │ │ │ ├── MediaTypeHeaderValues.cs │ │ │ ├── ProtobufInputFormatter.cs │ │ │ ├── ProtobufOutputFormatter.cs │ │ │ └── Surrogates │ │ │ │ └── DateTimeOffsetSurrogate.cs │ │ ├── Serializer │ │ │ └── ProtobufSerializer.cs │ │ └── Web.Serialization.Protobuf.csproj │ ├── Web.Testing │ │ ├── BaseApiClientTests.cs │ │ ├── BaseApiTestsFixture.cs │ │ ├── BaseServiceClientTests.cs │ │ ├── CustomAutofacServiceProviderFactory.cs │ │ ├── Extensions │ │ │ ├── ConfigurationBuilderExtensions.cs │ │ │ ├── MockLoggerExtensions.cs │ │ │ └── WebHostBuilderExtensions.cs │ │ └── Web.Testing.csproj │ └── Web │ │ ├── Configuration │ │ └── ConfigurationBuilderExtensions.cs │ │ ├── Documentation │ │ └── AuthResponsesOperationFilter.cs │ │ ├── ExceptionsHandling │ │ ├── ApplicationBuilderExtensions.cs │ │ ├── MvcOptionsExtensions.cs │ │ ├── ServiceCollectionExtensions.cs │ │ ├── UnhandledExceptionsFilterAttribute.cs │ │ ├── UnhandledExceptionsLoggingMiddleware.cs │ │ └── UnhandledExceptionsStartupFilter.cs │ │ ├── Routing │ │ ├── CentralPrefixConvention.cs │ │ └── MvcOptionsExtensions.cs │ │ ├── Validation │ │ ├── ModelValidationFilterAttribute.cs │ │ ├── MvcOptionsExtensions.cs │ │ └── ParametersValidationFilterAttribute.cs │ │ └── Web.csproj ├── Web.Application │ ├── Controllers │ │ ├── AccountController.cs │ │ └── ValuesController.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Services │ │ ├── TokenIssueEventHandler.cs │ │ └── UserClaimsProvider.cs │ └── Web.Application.csproj ├── Web.Client │ ├── ApiClient.Account.cs │ ├── ApiClient.Values.cs │ ├── ApiClient.cs │ ├── ApiClientOptions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ServicesClients │ │ ├── AccountController │ │ │ ├── AccountControllerClient.cs │ │ │ ├── AccountControllerClientOptions.cs │ │ │ └── ServiceCollectionExtensions.cs │ │ └── ValuesService │ │ │ ├── IValuesServiceClient.cs │ │ │ ├── ServiceCollectionExtensions.cs │ │ │ ├── ValuesServiceClient.cs │ │ │ └── ValuesServiceClientOptions.cs │ └── Web.Client.csproj ├── Web.DataAccess │ ├── Commands │ │ ├── DeleteValueAsyncCommand.cs │ │ └── SetValueCommand.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Queries │ │ ├── GetAllValuesQuery.cs │ │ └── GetValueQuery.cs │ ├── Repositories │ │ ├── IValuesRepository.cs │ │ └── Impl │ │ │ └── InMemoryValuesRepository.cs │ └── Web.DataAccess.csproj ├── Web.Domain │ ├── CommandContexts │ │ ├── DeleteValueAsyncCommandContext.cs │ │ └── SetValueCommandContext.cs │ ├── Criteria │ │ ├── GetAllValuesQueryCriterion.cs │ │ └── GetValueQueryCriterion.cs │ ├── Dtos │ │ └── DefaultConfigurationValues.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Web.Domain.csproj ├── Web.Migrations │ ├── Migration201703191215.cs │ └── Web.Migrations.csproj ├── Web.Models │ ├── Input │ │ └── ValuesModificationRequest.cs │ └── Web.Models.csproj └── Web │ ├── Installers │ ├── CommandsInstaller.cs │ ├── DataAccessInstaller.cs │ ├── QueriesInstaller.cs │ └── ServicesInstaller.cs │ ├── Program.cs │ ├── Properties │ ├── PublishProfiles │ │ ├── DevelopmentProfile.pubxml │ │ └── StagingProfile.pubxml │ └── launchSettings.json │ ├── Startup.cs │ ├── Web.Generic.config │ ├── Web.csproj │ ├── appsettings.Development.json │ ├── appsettings.Staging.json │ └── appsettings.json └── test ├── Infrastructure ├── CQRS.Implementations.Tests │ ├── CQRS.Implementations.Tests.csproj │ └── QueriesDispatcherTests.cs ├── Common.Tests │ ├── Common.Tests.csproj │ └── Extensions │ │ ├── DictionaryExtensionsTests.cs │ │ ├── DoubleExtensionsTests.cs │ │ ├── EnumExtensionsTests.cs │ │ ├── EnumerableExtensionsTests.cs │ │ └── ParallelExtensionsTests.cs ├── Dapper.Tests │ ├── Dapper.Tests.csproj │ ├── DbUsingTestBase.cs │ ├── Extensions │ │ └── SqlConnectionExtensionsTests.cs │ ├── SessionsFactory │ │ └── SessionsTests.cs │ ├── Tvp │ │ ├── PrimitiveValuesListTests.cs │ │ └── TvpParameterTests.cs │ └── TypesHandlers │ │ └── CollectionsHandlerTests.cs ├── Queues.RabbitMq.Tests │ ├── ExceptionHandlersFactoryTests.cs │ ├── GenericQueuesFactoryTests.cs │ ├── Handlers │ │ ├── CatchingMessageHandler.cs │ │ └── ThrowingExceptionMessageHandler.cs │ ├── MessagesProcessingServiceTests.cs │ ├── ProcessingService │ │ ├── NotificationsProcessingService.cs │ │ ├── NotificationsProcessingServiceOptions.cs │ │ └── ServiceCollectionExtensions.cs │ ├── QueueCreationOptionsExtensionsTests.cs │ ├── Queues.RabbitMq.Tests.csproj │ ├── RabbitQueueTests.cs │ ├── ServiceCollectionExtensionsTests.cs │ ├── appsettings.Development.Appveyor.Linux.json │ ├── appsettings.Development.Appveyor.Windows.json │ ├── appsettings.Development.Travis.json │ ├── appsettings.Development.json │ └── appsettings.json └── Web.Tests │ ├── Authentication │ └── JwtBearer │ │ ├── JwtBearerOptionsExtensionsTests.cs │ │ ├── TokenValidationParametersExtensionsTests.cs │ │ └── TokensIssuingOptionsExtensionsTests.cs │ ├── Conventions │ └── Responses │ │ └── ExceptionDescriptionTests.cs │ ├── ExceptionsHandling │ └── UnhandledExceptionsLoggingMiddlewareTests.cs │ ├── Integration │ └── BaseApiClient │ │ ├── HttpClientBuilderExtensionsTests.cs │ │ ├── HttpClientHandlerExtensionsTests.cs │ │ ├── HttpRequestHeadersExtensionsTests.cs │ │ ├── OptionsBuilderExtensionsTests.cs │ │ └── ServiceCollectionExtensionsTests.cs │ ├── Serialization │ ├── JsonNet │ │ ├── DateTimeConverterTests.cs │ │ └── SnakeCasePropertyNamesContractResolverTests.cs │ └── Protobuf │ │ ├── DateTimeOffsetSurrogateTests.cs │ │ └── RuntimeTypeModelExtensionsTests.cs │ └── Web.Tests.csproj └── Web.Tests ├── AccountApiClientTests.cs ├── ApiTestsCollection.cs ├── ServicesClientsTests.cs ├── ValuesApiClientTests.cs ├── ValuesControllerTests.cs ├── Web.Tests.csproj ├── appsettings.Development.json ├── appsettings.Staging.Appveyor.Linux.json ├── appsettings.Staging.Appveyor.Windows.json ├── appsettings.Staging.Travis.json ├── appsettings.Staging.json └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | _ReSharper.*/ 2 | build/artifacts/ 3 | build/tools/ 4 | bin 5 | obj 6 | Backup 7 | *.user 8 | *.suo 9 | *.Cache 10 | publish 11 | *.deploy 12 | Debug 13 | Release 14 | UpgradeLog.* 15 | _UpgradeReport_Files/ 16 | artifacts/ 17 | _UpgradeReport* 18 | *.dbmdl 19 | *.ncrunchproject 20 | *.ncrunchsolution 21 | /.vs/ 22 | project.lock.json 23 | logs 24 | web.config 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dmitriy Litichevskiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | image: Visual Studio 2019 3 | configuration: Release 4 | environment: 5 | ASPNETCORE_ENVIRONMENT: Staging 6 | deploy: off 7 | branches: 8 | only: 9 | - develop 10 | - /release.*/ 11 | - master 12 | skip_commits: 13 | files: 14 | - LICENSE 15 | - README.md 16 | - .travis.yml 17 | - WebInfrastructure.sln.DotSettings 18 | services: mssql2017 19 | install: 20 | - ps: choco install rabbitmq --version 3.7.7 -y 21 | build_script: 22 | - ps: >- 23 | cd build 24 | 25 | .\build.ps1 -target Test --settings_skipverification=true 26 | -------------------------------------------------------------------------------- /build/NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /build/build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [string]$Script = "build.cake", 4 | [string]$Target = "Default", 5 | [ValidateSet("Release", "Debug")] 6 | [string]$Configuration = "Release", 7 | [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] 8 | [string]$Verbosity = "Verbose", 9 | [Alias("DryRun","Noop")] 10 | [switch]$WhatIf, 11 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 12 | [string[]]$ScriptArgs 13 | ) 14 | 15 | Write-Host -f Yellow "Preparing to run build script..." 16 | if(!$PSScriptRoot){ 17 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 18 | } 19 | 20 | $UseDryRun = ""; 21 | if($WhatIf.IsPresent) 22 | { 23 | $UseDryRun = "--dryrun" 24 | } 25 | 26 | Write-Host -f Yellow "Looking for Cake.Tool..." 27 | if (-Not (& dotnet tool list -g | Select-String "cake.tool")) { 28 | & dotnet tool install --global --configfile ./NuGet.config Cake.Tool 29 | } 30 | 31 | Write-Host -f Yellow "Running build script..." 32 | & dotnet cake $Script --nuget_useinprocessclient=true --target=$Target --configuration=$Configuration --verbosity=$Verbosity $UseDryRun $ScriptArgs 33 | exit $LASTEXITCODE 34 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT="build.cake" 4 | TARGET="Default" 5 | CONFIGURATION="Release" 6 | VERBOSITY="verbose" 7 | DRYRUN= 8 | SHOW_VERSION=false 9 | SCRIPT_ARGUMENTS=() 10 | 11 | while [[ $# -gt 0 ]]; do 12 | case $1 in 13 | -s|--script) SCRIPT="$2"; shift ;; 14 | -t|--target) TARGET="$2"; shift ;; 15 | -c|--configuration) CONFIGURATION="$2"; shift ;; 16 | -v|--verbosity) VERBOSITY="$2"; shift ;; 17 | -d|--dryrun) DRYRUN="-dryrun" ;; 18 | --version) SHOW_VERSION=true ;; 19 | --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;; 20 | *) SCRIPT_ARGUMENTS+=("$1") ;; 21 | esac 22 | shift 23 | done 24 | 25 | if [[ $(dotnet tool list -g) != *"cake.tool"* ]]; then 26 | dotnet tool install --global --configfile ./NuGet.config Cake.Tool 27 | fi 28 | 29 | if $SHOW_VERSION; then 30 | dotnet cake --version 31 | else 32 | dotnet cake $SCRIPT --nuget_useinprocessclient=true --settings_skipverification=true --verbosity=$VERBOSITY --configuration=$CONFIGURATION --target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" 33 | fi -------------------------------------------------------------------------------- /common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.0.1 4 | $([System.DateTime]::op_Subtraction($([System.DateTime]::get_UtcNow().get_Date()),$([System.DateTime]::new(2018,1,1))).get_TotalDays()) 5 | $(VersionPrefix).$(Build) 6 | $(VersionPrefix).$(Build) 7 | 8 | -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/CQRS.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with abstractions for CQRS pattern 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard1.1 12 | Skeleton.CQRS.Abstractions 13 | Skeleton.CQRS.Abstractions 14 | Skeleton.CQRS.Abstractions 15 | CQRS 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Commands/IAsyncCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Commands 2 | { 3 | using System.Threading.Tasks; 4 | 5 | /// 6 | /// Interface for asynchronous commands 7 | /// 8 | /// Command context type 9 | public interface IAsyncCommand where TCommandContext : ICommandContext 10 | { 11 | /// 12 | /// Method for command execution 13 | /// 14 | /// Information needed for command execution 15 | Task Execute(TCommandContext commandContext); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Commands/ICommand.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Commands 2 | { 3 | /// 4 | /// Interface for synchronous commands 5 | /// 6 | /// Command context type 7 | public interface ICommand where TCommandContext : ICommandContext 8 | { 9 | /// 10 | /// Method for command execution 11 | /// 12 | /// Information needed for command execution 13 | void Execute(TCommandContext commandContext); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Commands/ICommandContext.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Commands 2 | { 3 | /// 4 | /// Interface for commands contexts marking 5 | /// 6 | public interface ICommandContext 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Commands/ICommandsDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Commands 2 | { 3 | using System.Threading.Tasks; 4 | 5 | /// 6 | /// Commands dispatcher interface 7 | /// 8 | public interface ICommandsDispatcher 9 | { 10 | /// 11 | /// Method for synchronous commands execution 12 | /// 13 | /// Command context type 14 | /// Information needed for commands execution 15 | void Execute(TCommandContext commandContext) where TCommandContext : ICommandContext; 16 | 17 | /// 18 | /// Method for asynchronous commands execution 19 | /// 20 | /// Command context type 21 | /// Information needed for commands execution 22 | Task ExecuteAsync(TCommandContext commandContext) where TCommandContext : ICommandContext; 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Queries/IAsyncCriterion.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Queries 2 | { 3 | using System.Threading.Tasks; 4 | public interface IAsyncCriterion : ICriterion> 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Queries/IAsyncQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Queries 2 | { 3 | using System.Threading.Tasks; 4 | 5 | /// 6 | /// Interface for asynchronously processed queries 7 | /// 8 | /// Query criterion type 9 | /// Query result type 10 | public interface IAsyncQuery : IQuery> where TCriterion : IAsyncCriterion 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Queries/ICriterion.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Queries 2 | { 3 | /// 4 | /// Interface for queries criterions marking 5 | /// 6 | public interface ICriterion 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Queries/IQueriesDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Queries 2 | { 3 | /// 4 | /// Queries dispatcher interface 5 | /// 6 | public interface IQueriesDispatcher 7 | { 8 | /// 9 | /// Method for queries execution 10 | /// 11 | /// Query result type 12 | /// Information needed for queries execution 13 | /// Query result 14 | TResult Execute(ICriterion criterion); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Queries/IQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Queries 2 | { 3 | /// 4 | /// Interface for queries 5 | /// 6 | /// Query criterion type 7 | /// Query result type 8 | public interface IQuery where TCriterion : ICriterion 9 | { 10 | /// 11 | /// Method for criterion execution 12 | /// 13 | /// Information needed for criterion execution 14 | /// Query result 15 | TResult Ask(TCriterion criterion); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Abstractions/Queries/QueriesDispatcherExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Abstractions.Queries 2 | { 3 | using System.Threading.Tasks; 4 | 5 | /// 6 | /// Extensions for queries dispatcher 7 | /// 8 | public static class QueriesDispatcherExtensions 9 | { 10 | /// 11 | /// Method for asynchronous queries execution 12 | /// 13 | /// Query result type 14 | /// Queries dispatcher 15 | /// Information needed for queries execution 16 | /// Task for asynchronous operation 17 | public static Task ExecuteAsync(this IQueriesDispatcher queriesDispatcher, IAsyncCriterion criterion) 18 | { 19 | return queriesDispatcher.Execute(criterion); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Implementations/CQRS.Implementations.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with implementations of CQRS pattern 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard1.1 12 | Skeleton.CQRS.Implementations 13 | Skeleton.CQRS.Implementations 14 | Skeleton.CQRS.Implementations 15 | CQRS 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Implementations/Commands/CommandsFactory/GenericCommandsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Implementations.Commands.CommandsFactory 2 | { 3 | using System; 4 | using Abstractions.Commands; 5 | 6 | /// 7 | /// Commands factory implementation based on IServiceProvider 8 | /// 9 | public class GenericCommandsFactory : ICommandsFactory 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | 13 | /// 14 | /// Constructor 15 | /// 16 | public GenericCommandsFactory(IServiceProvider serviceProvider) 17 | { 18 | if(serviceProvider == null) 19 | throw new ArgumentNullException(nameof(serviceProvider)); 20 | 21 | _serviceProvider = serviceProvider; 22 | } 23 | 24 | /// 25 | public ICommand CreateCommand() where TCommandContext : ICommandContext 26 | { 27 | return (ICommand)_serviceProvider.GetService(typeof(ICommand)); 28 | } 29 | 30 | /// 31 | public IAsyncCommand CreateAsyncCommand() where TCommandContext : ICommandContext 32 | { 33 | return (IAsyncCommand)_serviceProvider.GetService(typeof(IAsyncCommand)); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Implementations/Commands/CommandsFactory/ICommandsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Implementations.Commands.CommandsFactory 2 | { 3 | using Abstractions.Commands; 4 | 5 | /// 6 | /// Commands factory interface 7 | /// 8 | public interface ICommandsFactory 9 | { 10 | /// 11 | /// Method for synchronous commands creation 12 | /// 13 | /// Command context type 14 | /// Command instance 15 | ICommand CreateCommand() where TCommandContext : ICommandContext; 16 | 17 | /// 18 | /// Method for asynchronous commands creation 19 | /// 20 | /// Command context type 21 | /// Command instance 22 | IAsyncCommand CreateAsyncCommand() where TCommandContext : ICommandContext; 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Implementations/Queries/QueriesFactory/GenericQueriesFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Implementations.Queries.QueriesFactory 2 | { 3 | using System; 4 | using Abstractions.Queries; 5 | 6 | /// 7 | /// Queries factory based on IServiceProvider 8 | /// 9 | public class GenericQueriesFactory : IQueriesFactory 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | 13 | /// 14 | /// Constructor 15 | /// 16 | public GenericQueriesFactory(IServiceProvider serviceProvider) 17 | { 18 | if(serviceProvider == null) 19 | throw new ArgumentNullException(nameof(serviceProvider)); 20 | 21 | _serviceProvider = serviceProvider; 22 | } 23 | 24 | /// 25 | public IQuery Create() where TCriterion : ICriterion 26 | { 27 | return (IQuery)_serviceProvider.GetService(typeof(IQuery)); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Infrastructure/CQRS.Implementations/Queries/QueriesFactory/IQueriesFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.CQRS.Implementations.Queries.QueriesFactory 2 | { 3 | using Abstractions.Queries; 4 | 5 | /// 6 | /// Queries factory interface 7 | /// 8 | public interface IQueriesFactory 9 | { 10 | /// 11 | /// Method for qsueries creation 12 | /// 13 | /// Query criterion type 14 | /// Query result type 15 | /// Query instance 16 | IQuery Create() where TCriterion : ICriterion; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Infrastructure/Common/Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with common extensions and objects for applications development 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard1.6 12 | Skeleton.Common 13 | Skeleton.Common 14 | Skeleton.Common 15 | Common extensions 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Infrastructure/Common/Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Common.Extensions 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public static class DictionaryExtensions 7 | { 8 | public static TValue GetOrDefault(this IReadOnlyDictionary dictionary, TKey key) 9 | { 10 | return dictionary.TryGetValue(key, out var value) ? value : default(TValue); 11 | } 12 | 13 | public static TValue GetOrEmpty(this IReadOnlyDictionary dictionary, TKey key) where TValue : new() 14 | { 15 | return dictionary.TryGetValue(key, out var value) ? value : new TValue(); 16 | } 17 | 18 | public static TValue GetOrValue(this IReadOnlyDictionary dictionary, TKey key, TValue replacement) 19 | { 20 | return dictionary.TryGetValue(key, out var value) ? value : replacement; 21 | } 22 | 23 | public static TValue GetOrValue( 24 | this IReadOnlyDictionary dictionary, 25 | TKey key, 26 | Func valueFunc) 27 | { 28 | return dictionary.TryGetValue(key, out var value) ? value : valueFunc(key); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Infrastructure/Common/Extensions/DoubleExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Common.Extensions 2 | { 3 | using System; 4 | 5 | public static class DoubleExtensions 6 | { 7 | public const double DefaultAccuracy = 1e-7; 8 | 9 | private static double GetUsedAccuracy(double? accuracy) => accuracy ?? DefaultAccuracy; 10 | 11 | public static bool Equal(this double first, double second, double? accuracy = null) 12 | { 13 | var usedAccuracy = GetUsedAccuracy(accuracy); 14 | return first <= second + usedAccuracy 15 | && first + usedAccuracy >= second; 16 | } 17 | 18 | public static bool NotEqual(this double first, double second, double? accuracy = null) 19 | { 20 | var usedAccuracy = GetUsedAccuracy(accuracy); 21 | return first + usedAccuracy < second 22 | || first > second + usedAccuracy; 23 | } 24 | 25 | public static bool LessOrEqual(this double first, double second, double? accuracy = null) 26 | { 27 | return first <= second + GetUsedAccuracy(accuracy); 28 | } 29 | 30 | public static bool Less(this double first, double second, double? accuracy = null) 31 | { 32 | return first + GetUsedAccuracy(accuracy) < second; 33 | } 34 | 35 | public static bool GreaterOrEqual(this double first, double second, double? accuracy = null) 36 | { 37 | return first + GetUsedAccuracy(accuracy) >= second; 38 | } 39 | 40 | public static bool Greater(this double first, double second, double? accuracy = null) 41 | { 42 | return first > second + GetUsedAccuracy(accuracy); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Infrastructure/Common/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Common.Extensions 2 | { 3 | using System; 4 | using System.ComponentModel; 5 | using System.Reflection; 6 | 7 | public static class EnumExtensions 8 | { 9 | public static string GetDescription(this Enum member) 10 | { 11 | var memberTypeInfo = member.GetType().GetTypeInfo(); 12 | 13 | if (memberTypeInfo.IsEnum == false) 14 | throw new ArgumentOutOfRangeException(nameof(member), "member is not enum"); 15 | 16 | var fieldInfo = memberTypeInfo.GetField(member.ToString()); 17 | if (fieldInfo == null) 18 | return null; 19 | 20 | var attributes = fieldInfo.GetCustomAttributes(false).AsArray(); 21 | 22 | if (attributes.Length > 0) 23 | return attributes[0].Description; 24 | 25 | return member.ToString(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Infrastructure/Common/Extensions/ParallelExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Common.Extensions 2 | { 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | public static class ParallelExtensions 11 | { 12 | public static IEnumerable> ForEachAsync( 13 | this IEnumerable source, 14 | Func body, 15 | int degreeOfParallelism) 16 | { 17 | if (source == null) 18 | throw new ArgumentNullException(nameof(source)); 19 | if (body == null) 20 | throw new ArgumentNullException(nameof(body)); 21 | 22 | var sourceArray = source.ToArray(); 23 | var completedTask = -1; 24 | 25 | var taskCompletions = new TaskCompletionSource[sourceArray.Length]; 26 | for (var i = 0; i < taskCompletions.Length; i++) 27 | taskCompletions[i] = new TaskCompletionSource(); 28 | 29 | foreach (var partition in Partitioner.Create(sourceArray).GetPartitions(degreeOfParallelism)) 30 | Task.Run( 31 | () => 32 | { 33 | while (partition.MoveNext()) 34 | { 35 | var result = body(partition.Current); 36 | 37 | var finishedTaskIndex = Interlocked.Increment(ref completedTask); 38 | taskCompletions[finishedTaskIndex].SetResult(result); 39 | } 40 | } 41 | ); 42 | 43 | return taskCompletions.Select(tcs => tcs.Task); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/ConnectionsFactory/IConnectionsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.ConnectionsFactory 2 | { 3 | using System.Data; 4 | 5 | public interface IConnectionsFactory 6 | { 7 | IDbConnection Create(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/ConnectionsFactory/SqlConnectionsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.ConnectionsFactory 2 | { 3 | using System; 4 | using System.Data; 5 | using System.Data.SqlClient; 6 | using Microsoft.Extensions.Options; 7 | 8 | public class SqlConnectionsFactory : IConnectionsFactory 9 | { 10 | private readonly SqlConnectionsFactoryOptions _options; 11 | 12 | public SqlConnectionsFactory(IOptions options) 13 | { 14 | if (options == null) 15 | throw new ArgumentNullException(nameof(options)); 16 | if (string.IsNullOrWhiteSpace(options.Value.SqlServer)) 17 | throw new ArgumentNullException(nameof(SqlConnectionsFactoryOptions.SqlServer)); 18 | 19 | _options = options.Value; 20 | } 21 | 22 | public IDbConnection Create() 23 | { 24 | var sqlConnection = new SqlConnection(_options.SqlServer); 25 | sqlConnection.Open(); 26 | return sqlConnection; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/ConnectionsFactory/SqlConnectionsFactoryOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.ConnectionsFactory 2 | { 3 | public class SqlConnectionsFactoryOptions 4 | { 5 | public string SqlServer { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Dapper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with common extensions and helpers for use Dapper in code 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard2.0 12 | Skeleton.Dapper 13 | Skeleton.Dapper 14 | Skeleton.Dapper 15 | Helpers for Dapper;TVP;Bulk Copy 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Extensions/IColumnsMappingConfigurator.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Extensions 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | public interface IColumnsMappingConfigurator where TEntity : class 7 | { 8 | IColumnsMappingConfigurator WithProperty( 9 | Expression> member, 10 | string tableColumnName = null); 11 | 12 | IColumnsMappingConfigurator WithFunction( 13 | Func valuesProvider, 14 | string tableColumnName); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Extensions/IMappingInfoProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Extensions 2 | { 3 | using System.Data.SqlClient; 4 | 5 | public interface IMappingInfoProvider 6 | { 7 | object GetValue(int ordinal, object current); 8 | SqlBulkCopyColumnMappingCollection MappingsCollection { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Extensions/PropertyInfoProviders/ExpandoObjectMappingInfoProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Extensions.PropertyInfoProviders 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data.SqlClient; 6 | 7 | public class ExpandoObjectMappingInfoProvider : IMappingInfoProvider 8 | { 9 | private readonly Dictionary _propertiesKeyIndices; 10 | 11 | public ExpandoObjectMappingInfoProvider( 12 | IDictionary properties, 13 | SqlBulkCopyColumnMappingCollection mappingsCollection) 14 | { 15 | if (properties == null || properties.Count == 0) 16 | throw new InvalidOperationException("Cannot collect properties from object"); 17 | 18 | MappingsCollection = mappingsCollection; 19 | _propertiesKeyIndices = new Dictionary(); 20 | 21 | foreach (var property in properties) 22 | { 23 | var ordinal = _propertiesKeyIndices.Count; 24 | 25 | _propertiesKeyIndices.Add(ordinal, property.Key); 26 | MappingsCollection.Add(new SqlBulkCopyColumnMapping(ordinal, property.Key)); 27 | } 28 | } 29 | 30 | public SqlBulkCopyColumnMappingCollection MappingsCollection { get; } 31 | 32 | public object GetValue(int ordinal, object collectionEnumeratorCurrent) 33 | { 34 | var propertyName = _propertiesKeyIndices[ordinal]; 35 | return ((IDictionary)collectionEnumeratorCurrent)[propertyName]; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Extensions/PropertyInfoProviders/StrictTypeMappingInfoProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Extensions.PropertyInfoProviders 2 | { 3 | using System; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | public class StrictTypeMappingInfoProvider : IMappingInfoProvider 9 | { 10 | private readonly PropertyInfo[] _itemProperties; 11 | public StrictTypeMappingInfoProvider(Type type, SqlBulkCopyColumnMappingCollection mappingsCollection) 12 | { 13 | _itemProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); 14 | if (_itemProperties == null || _itemProperties.Any() == false) 15 | throw new InvalidOperationException("Cannot collect properties from object"); 16 | 17 | MappingsCollection = mappingsCollection; 18 | for (var i = 0; i < _itemProperties.Length; i++) 19 | MappingsCollection.Add(new SqlBulkCopyColumnMapping(i, _itemProperties[i].Name)); 20 | } 21 | 22 | public SqlBulkCopyColumnMappingCollection MappingsCollection { get; } 23 | 24 | public object GetValue(int ordinal, object current) 25 | { 26 | return _itemProperties[ordinal].GetValue(current); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/QueryObject.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper 2 | { 3 | using System; 4 | 5 | /// 6 | /// Incapsulate SQL and Parameters for Dapper methods 7 | /// 8 | /// 9 | /// http://www.martinfowler.com/eaaCatalog/queryObject.html 10 | /// 11 | public class QueryObject 12 | { 13 | /// 14 | /// SQL string 15 | /// 16 | public string Sql { get; private set; } 17 | 18 | /// 19 | /// Parameter list 20 | /// 21 | public object QueryParams { get; private set; } 22 | 23 | /// 24 | /// Create QueryObject for parameterized 25 | /// 26 | /// SQL string 27 | /// Parameter list 28 | public QueryObject(string sql, object queryParams = null) 29 | { 30 | if (string.IsNullOrEmpty(sql)) 31 | throw new ArgumentNullException(nameof(sql)); 32 | 33 | Sql = sql; 34 | QueryParams = queryParams; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/SessionsFactory/ISession.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.SessionsFactory 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Threading.Tasks; 7 | using Extensions; 8 | 9 | public interface ISession : IDisposable 10 | { 11 | IEnumerable Query(string sql, object param = null, bool buffered = true, TimeSpan? commandTimeout = null, 12 | CommandType? commandType = null); 13 | 14 | Task> QueryAsync(string sql, object param = null, TimeSpan? commandTimeout = null, 15 | CommandType? commandType = null); 16 | 17 | int Execute(string sql, object param = null, TimeSpan? commandTimeout = null, CommandType? commandType = null); 18 | Task ExecuteAsync(string sql, object param = null, TimeSpan? commandTimeout = null, CommandType? commandType = null); 19 | 20 | void BulkInsert(string tableName, IReadOnlyCollection source, 21 | Func, IColumnsMappingConfigurator> columnsMappingSetup = null, 22 | int? batchSize = null, TimeSpan? timeout = null) where TSource : class; 23 | 24 | void Commit(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/SessionsFactory/ISessionsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.SessionsFactory 2 | { 3 | using System.Data; 4 | 5 | public interface ISessionsFactory 6 | { 7 | ISession Create(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/SessionsFactory/SessionsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.SessionsFactory 2 | { 3 | using System; 4 | using System.Data; 5 | using ConnectionsFactory; 6 | 7 | public class SessionsFactory : ISessionsFactory 8 | { 9 | private readonly IConnectionsFactory _connectionsFactory; 10 | 11 | public SessionsFactory(IConnectionsFactory connectionsFactory) 12 | { 13 | if (connectionsFactory == null) 14 | throw new ArgumentNullException(nameof(connectionsFactory)); 15 | 16 | _connectionsFactory = connectionsFactory; 17 | } 18 | 19 | public ISession Create(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted) 20 | { 21 | var connection = _connectionsFactory.Create(); 22 | var transaction = connection.BeginTransaction(isolationLevel); 23 | 24 | return new Session(connection, transaction); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Tvp/IMetaDataOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Tvp 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | public interface IMetaDataOptionsBuilder where TSource : class 7 | { 8 | IMetaDataOptionsBuilder SetAccuracy(Expression> member, byte precision, byte scale); 9 | 10 | IMetaDataOptionsBuilder SetAccuracy(Expression> member, byte precision, byte scale); 11 | 12 | IMetaDataOptionsBuilder SetMaxLength(Expression> member, long maxLength); 13 | 14 | IMetaDataOptionsBuilder SetMaxLength(Expression> member, long maxLength); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Tvp/Int32ValuesList.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Tvp 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Int32ValuesList : PrimitiveValuesList 6 | { 7 | public Int32ValuesList(string typeName, IEnumerable source) : base(typeName, source) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Tvp/Int64ValuesList.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Tvp 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Int64ValuesList : PrimitiveValuesList 6 | { 7 | public Int64ValuesList(string typeName, IEnumerable source) : base(typeName, source) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Dapper/Tvp/MetaDataCreationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Tvp 2 | { 3 | public class MetaDataCreationOptions 4 | { 5 | public long? MaxLength { get; set; } 6 | 7 | public byte? Precision { get; set; } 8 | 9 | public byte? Scale { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | portable 7 | dmitriy.litichevskiy 8 | https://github.com/litichevskiydv/WebInfrastructure 9 | MIT 10 | git 11 | https://github.com/litichevskiydv/WebInfrastructure 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/Configuration/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.Configuration 2 | { 3 | using System; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Options; 7 | 8 | public static class ServiceCollectionExtensions 9 | { 10 | public static IServiceCollection ConfigureMessagesProcessingService( 11 | this IServiceCollection services, 12 | IConfiguration configuration, 13 | Action> optionsConfigurator) 14 | where TServiceOptions : MessagesProcessingServiceOptions 15 | { 16 | if(services == null) 17 | throw new ArgumentNullException(nameof(services)); 18 | if (configuration == null) 19 | throw new ArgumentNullException(nameof(configuration)); 20 | if (optionsConfigurator == null) 21 | throw new ArgumentNullException(nameof(optionsConfigurator)); 22 | 23 | services.Configure(configuration); 24 | optionsConfigurator(services.AddOptions()); 25 | 26 | return services; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/IMessageHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | public interface IMessageHandler 7 | { 8 | Task Handle(TMessage message, CancellationToken cancellationToken); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/MessagesProcessingServiceOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions 2 | { 3 | using System; 4 | using QueuesFactory.Configuration; 5 | 6 | public abstract class MessagesProcessingServiceOptions 7 | { 8 | public TimeSpan ConnectionAttemptTimeout { get; set; } 9 | 10 | public QueueCreationOptions QueueCreationOptions { get; set; } 11 | 12 | internal MessagesProcessingServiceOptions() 13 | { 14 | } 15 | } 16 | 17 | public abstract class MessagesProcessingServiceOptions : MessagesProcessingServiceOptions 18 | where TQueueCreationOptions : QueueCreationOptions 19 | { 20 | public new TQueueCreationOptions QueueCreationOptions 21 | { 22 | get => (TQueueCreationOptions)base.QueueCreationOptions; 23 | set => base.QueueCreationOptions = value; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/Queues.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with abstractions for working with message queues 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard2.0 12 | Skeleton.Queues.Abstractions 13 | Skeleton.Queues.Abstractions 14 | Skeleton.Queues.Abstractions 15 | Queues 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/Configuration/QueueCreationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.Configuration 2 | { 3 | using System; 4 | using ExceptionsHandling; 5 | using ExceptionsHandling.Handlers; 6 | 7 | public abstract class QueueCreationOptions 8 | { 9 | public string QueueName { get; set; } 10 | 11 | public int RetriesCount { get; set; } 12 | 13 | public TimeSpan RetryInitialTimeout { get; set; } 14 | 15 | public ExceptionHandlingPolicy? ExceptionHandlingPolicy { get; set; } 16 | 17 | internal QueueCreationOptions() 18 | { 19 | } 20 | } 21 | 22 | public abstract class QueueCreationOptions : QueueCreationOptions 23 | where TMessageDescription : QueueMessageDescriptionBase, new() 24 | { 25 | public ExceptionHandlerBase ExceptionHandler { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/Configuration/QueueCreationOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.Configuration 2 | { 3 | using System; 4 | using ExceptionsHandling.Handlers; 5 | 6 | public static class QueueCreationOptionsExtensions 7 | { 8 | public static TQueueCreationOptions WithExceptionHandler( 9 | this TQueueCreationOptions queueCreationOptions, 10 | ExceptionHandlerBase exceptionHandler) 11 | where TQueueCreationOptions : QueueCreationOptions 12 | where TMessageDescription : QueueMessageDescriptionBase, new() 13 | { 14 | if(queueCreationOptions == null) 15 | throw new ArgumentNullException(nameof(queueCreationOptions)); 16 | if (exceptionHandler == null) 17 | throw new ArgumentNullException(nameof(exceptionHandler)); 18 | 19 | queueCreationOptions.ExceptionHandler = exceptionHandler; 20 | return queueCreationOptions; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/ErrorInformation.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling 2 | { 3 | public class ErrorInformation 4 | { 5 | public string MessageId { get; set; } 6 | 7 | public string MessageContent { get; set; } 8 | 9 | public string ExceptionDescription { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/ExceptionHandlersFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using Handlers; 7 | using QueuesFactory; 8 | 9 | public class ExceptionHandlersFactory : IExceptionHandlersFactory 10 | where TMessageDescription : QueueMessageDescriptionBase, new() 11 | { 12 | private readonly IReadOnlyDictionary> _supportedPolicies; 13 | 14 | public ExceptionHandlersFactory(IEnumerable> registerdHandlers) 15 | { 16 | if(registerdHandlers == null) 17 | throw new ArgumentNullException(nameof(registerdHandlers)); 18 | 19 | var registeredHandlersArray = registerdHandlers.ToArray(); 20 | if (registeredHandlersArray.GroupBy(x => x.ExceptionHandlingPolicy).Any(x => x.Count() > 1)) 21 | throw new InvalidOperationException("More than one handler was registered for policy"); 22 | 23 | _supportedPolicies = registeredHandlersArray.ToDictionary(x => x.ExceptionHandlingPolicy); 24 | } 25 | 26 | public ExceptionHandlerBase GetHandler(ExceptionHandlingPolicy policy) 27 | { 28 | if(_supportedPolicies.TryGetValue(policy, out var handler) == false) 29 | throw new NotSupportedException($"Policy {policy.ToString()} is not supported"); 30 | 31 | return handler; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/ExceptionHandlingPolicy.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling 2 | { 3 | public enum ExceptionHandlingPolicy 4 | { 5 | None = 0, 6 | Requeue = 1, 7 | SendToErrorsQueue = 2, 8 | Custom = 3 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/Handlers/EmptyExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling.Handlers 2 | { 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Logging; 7 | using QueuesFactory; 8 | 9 | public class EmptyExceptionHandler : ExceptionHandlerBase 10 | where TMessageDescription : QueueMessageDescriptionBase, new() 11 | { 12 | public EmptyExceptionHandler(ILogger> logger) 13 | : base(logger, ExceptionHandlingPolicy.None) 14 | { 15 | } 16 | 17 | protected override async Task HandleExceptionAsync( 18 | QueueBase queue, 19 | TMessageDescription messageDescription, 20 | Exception exception, 21 | CancellationToken cancellationToken) 22 | { 23 | await queue.AcknowledgeMessageAsync(messageDescription, cancellationToken); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/Handlers/ErrorsQueuingExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling.Handlers 2 | { 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Logging; 7 | using QueuesFactory; 8 | 9 | public class ErrorsQueuingExceptionHandler : ExceptionHandlerBase 10 | where TMessageDescription : QueueMessageDescriptionBase, new() 11 | { 12 | public ErrorsQueuingExceptionHandler(ILogger> logger) 13 | : base(logger, ExceptionHandlingPolicy.SendToErrorsQueue) 14 | { 15 | } 16 | 17 | protected override async Task HandleExceptionAsync( 18 | QueueBase queue, 19 | TMessageDescription messageDescription, 20 | Exception exception, 21 | CancellationToken cancellationToken) 22 | { 23 | 24 | await queue.AcknowledgeMessageAsync(messageDescription, cancellationToken); 25 | await queue.ErrorsQueue.SendMessageAsync( 26 | new ErrorInformation 27 | { 28 | ExceptionDescription = exception.ToString(), 29 | MessageId = messageDescription.Id, 30 | MessageContent = messageDescription.Content 31 | }, 32 | cancellationToken 33 | ); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/Handlers/ExceptionHandlerBase.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling.Handlers 2 | { 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Logging; 7 | 8 | public abstract class ExceptionHandlerBase 9 | where TMessageDescription : QueueMessageDescriptionBase, new() 10 | { 11 | protected readonly ILogger Logger; 12 | public ExceptionHandlingPolicy ExceptionHandlingPolicy { get; } 13 | 14 | protected ExceptionHandlerBase(ILogger logger, ExceptionHandlingPolicy exceptionHandlingPolicy) 15 | { 16 | if (logger == null) 17 | throw new ArgumentNullException(nameof(logger)); 18 | 19 | Logger = logger; 20 | ExceptionHandlingPolicy = exceptionHandlingPolicy; 21 | } 22 | 23 | protected abstract Task HandleExceptionAsync( 24 | QueueBase queue, 25 | TMessageDescription messageDescription, 26 | Exception exception, 27 | CancellationToken cancellationToken); 28 | 29 | public async Task HandleAsync( 30 | QueueBase queue, 31 | TMessageDescription messageDescription, 32 | Exception exception, 33 | CancellationToken cancellationToken) 34 | { 35 | try 36 | { 37 | await HandleExceptionAsync(queue, messageDescription, exception, cancellationToken); 38 | } 39 | catch (Exception e) 40 | { 41 | Logger.LogError(e, "Error during has occurred during handling previous exception"); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/Handlers/RequeuingExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling.Handlers 2 | { 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Logging; 7 | using QueuesFactory; 8 | 9 | public class RequeuingExceptionHandler : ExceptionHandlerBase 10 | where TMessageDescription : QueueMessageDescriptionBase, new() 11 | { 12 | public RequeuingExceptionHandler(ILogger> logger) 13 | : base(logger, ExceptionHandlingPolicy.Requeue) 14 | { 15 | } 16 | 17 | protected override async Task HandleExceptionAsync( 18 | QueueBase queue, 19 | TMessageDescription messageDescription, 20 | Exception exception, 21 | CancellationToken cancellationToken) 22 | { 23 | await queue.RejectMessageAsync(messageDescription, cancellationToken); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ExceptionsHandling/IExceptionHandlersFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory.ExceptionsHandling 2 | { 3 | using Handlers; 4 | using QueuesFactory; 5 | 6 | public interface IExceptionHandlersFactory where TMessageDescription : QueueMessageDescriptionBase, new() 7 | { 8 | ExceptionHandlerBase GetHandler(ExceptionHandlingPolicy policy); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/GenericQueuesFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory 2 | { 3 | using System; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.ExceptionServices; 7 | using Configuration; 8 | 9 | public class GenericQueuesFactory : IGenericQueuesFactory 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | private readonly Type _typedQueuesFactoryType; 13 | 14 | public GenericQueuesFactory(IServiceProvider serviceProvider) 15 | { 16 | if(serviceProvider == null) 17 | throw new ArgumentNullException(nameof(serviceProvider)); 18 | 19 | _serviceProvider = serviceProvider; 20 | _typedQueuesFactoryType = typeof(ITypedQueuesFactory<>); 21 | } 22 | 23 | public ITypedQueue Create(QueueCreationOptions creationOptions) 24 | { 25 | var creationOptionsType = creationOptions.GetType(); 26 | var typedQueuesFactory = _serviceProvider.GetService(_typedQueuesFactoryType.MakeGenericType(creationOptionsType)); 27 | var createMethodDefinition = typedQueuesFactory.GetType() 28 | .GetMethods() 29 | .Single(x => x.IsGenericMethod && x.Name == nameof(ITypedQueuesFactory.Create)) 30 | .MakeGenericMethod(typeof(TMessage)); 31 | 32 | try 33 | { 34 | return (ITypedQueue) createMethodDefinition.Invoke(typedQueuesFactory, new object[] {creationOptions}); 35 | } 36 | catch (TargetInvocationException ex) 37 | { 38 | ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); 39 | } 40 | 41 | return default(ITypedQueue); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/IGenericQueuesFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory 2 | { 3 | using Configuration; 4 | 5 | public interface IGenericQueuesFactory 6 | { 7 | ITypedQueue Create(QueueCreationOptions creationOptions); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ITypedQueue.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory 2 | { 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | public interface ITypedQueue : IDisposable 8 | { 9 | Task> SendMessageAsync(TMessage message, CancellationToken cancellationToken = default(CancellationToken)); 10 | 11 | Task> SubscribeAsync(IMessageHandler handler, CancellationToken cancellationToken = default(CancellationToken)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/ITypedQueuesFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory 2 | { 3 | using Configuration; 4 | 5 | public interface ITypedQueuesFactory where TQueueCreationOptions : QueueCreationOptions 6 | { 7 | ITypedQueue Create(TQueueCreationOptions creationOptions); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/IUntypedQueuesFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory 2 | { 3 | using Configuration; 4 | 5 | public interface IUntypedQueuesFactory 6 | where TQueueCreationOptions : QueueCreationOptions 7 | where TQueue : QueueBase 8 | { 9 | TQueue Create(TQueueCreationOptions creationOptions); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.Abstractions/QueuesFactory/QueueMessageDescriptionBase.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.Abstractions.QueuesFactory 2 | { 3 | public abstract class QueueMessageDescriptionBase 4 | { 5 | public string Id { get; set; } 6 | 7 | public string Content { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.RabbitMq/Queues.RabbitMq.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with classes for working with RabbitMq 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard2.0 12 | Skeleton.Queues.RabbitMq 13 | Skeleton.Queues.RabbitMq 14 | Skeleton.Queues.RabbitMq 15 | RabbitMq 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Infrastructure/Queues.RabbitMq/QueuesFactory/Configuration/RabbitCredentianls.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.QueuesFactory.Configuration 2 | { 3 | public class RabbitCredentianls 4 | { 5 | public string UserName { get; set; } 6 | 7 | public string Password { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.RabbitMq/QueuesFactory/Configuration/RabbitQueueCreationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.QueuesFactory.Configuration 2 | { 3 | using System.Collections.Generic; 4 | using Abstractions.QueuesFactory.Configuration; 5 | 6 | public class RabbitQueueCreationOptions : QueueCreationOptions 7 | { 8 | public IDictionary AdditionalArguments { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.RabbitMq/QueuesFactory/Configuration/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.QueuesFactory.Configuration 2 | { 3 | using System; 4 | using Abstractions.QueuesFactory; 5 | using Abstractions.QueuesFactory.ExceptionsHandling; 6 | using Abstractions.QueuesFactory.ExceptionsHandling.Handlers; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.DependencyInjection.Extensions; 9 | 10 | public static class ServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddRabbitMqSupport(this IServiceCollection serviceCollection) 13 | { 14 | if(serviceCollection == null) 15 | throw new ArgumentNullException(nameof(serviceCollection)); 16 | 17 | serviceCollection.AddSingleton, EmptyExceptionHandler>(); 18 | serviceCollection.AddSingleton, RequeuingExceptionHandler>(); 19 | serviceCollection.AddSingleton, ErrorsQueuingExceptionHandler>(); 20 | serviceCollection.TryAddSingleton, ExceptionHandlersFactory>(); 21 | serviceCollection.TryAddSingleton(); 22 | serviceCollection.TryAddSingleton>(x => x.GetService()); 23 | serviceCollection.TryAddSingleton>>(x => x.GetService()); 24 | serviceCollection.TryAddSingleton(); 25 | 26 | return serviceCollection; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.RabbitMq/QueuesFactory/Configuration/TypedRabbitQueuesFactoryOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.QueuesFactory.Configuration 2 | { 3 | using System; 4 | 5 | public class TypedRabbitQueuesFactoryOptions 6 | { 7 | public RabbitCredentianls Credentials { get; set; } 8 | 9 | public string[] Hosts { get; set; } 10 | 11 | public TimeSpan NetworkRecoveryInterval { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infrastructure/Queues.RabbitMq/QueuesFactory/RabbitMessageDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.QueuesFactory 2 | { 3 | using System.Collections.Generic; 4 | using Abstractions.QueuesFactory; 5 | 6 | public class RabbitMessageDescription : QueueMessageDescriptionBase 7 | { 8 | public IDictionary Headers { get; set; } 9 | 10 | public ulong? DeliveryTag { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/ApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer 2 | { 3 | using System; 4 | using Microsoft.AspNetCore.Builder; 5 | 6 | public static class ApplicationBuilderExtensions 7 | { 8 | public static IApplicationBuilder UseJwtBearerTokensIssuer(this IApplicationBuilder applicationBuilder) 9 | { 10 | if (applicationBuilder == null) 11 | throw new ArgumentNullException(nameof(applicationBuilder)); 12 | 13 | return applicationBuilder.UseMiddleware(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Configuration/Issuing/IJwtBearerTokensIssuerBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Configuration.Issuing 2 | { 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | public interface IJwtBearerTokensIssuerBuilder 6 | { 7 | IServiceCollection Services { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Configuration/Issuing/JwtBearerTokensIssuerBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Configuration.Issuing 2 | { 3 | using System; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | public class JwtBearerTokensIssuerBuilder : IJwtBearerTokensIssuerBuilder 7 | { 8 | public JwtBearerTokensIssuerBuilder(IServiceCollection services) 9 | { 10 | if (services == null) 11 | throw new ArgumentNullException(nameof(services)); 12 | 13 | Services = services; 14 | } 15 | 16 | public IServiceCollection Services { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Configuration/Issuing/JwtBearerTokensIssuerBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Configuration.Issuing 2 | { 3 | using System; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.DependencyInjection.Extensions; 6 | using Microsoft.Extensions.Options; 7 | 8 | public static class JwtBearerTokensIssuerBuilderExtensions 9 | { 10 | public static IJwtBearerTokensIssuerBuilder WithTokenIssueEventHandler( 11 | this IJwtBearerTokensIssuerBuilder issuerBuilder 12 | ) 13 | where TEventHandler : ITokenIssueEventHandler 14 | { 15 | if (issuerBuilder == null) 16 | throw new ArgumentNullException(nameof(issuerBuilder)); 17 | 18 | issuerBuilder.Services.TryAddEnumerable( 19 | ServiceDescriptor.Transient, TokenIssueEventHandlerSetup>() 20 | ); 21 | return issuerBuilder; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Configuration/Issuing/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Configuration.Issuing 2 | { 3 | using System; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IJwtBearerTokensIssuerBuilder AddJwtBearerTokensIssuer(this IServiceCollection services, Action configure) 9 | { 10 | if(services == null) 11 | throw new ArgumentNullException(nameof(services)); 12 | if(configure == null) 13 | throw new ArgumentNullException(nameof(configure)); 14 | 15 | services 16 | .AddOptions() 17 | .Configure(configure); 18 | 19 | return new JwtBearerTokensIssuerBuilder(services); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Configuration/Issuing/TokenIssueEventHandlerSetup.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Configuration.Issuing 2 | { 3 | using System; 4 | using Microsoft.Extensions.Options; 5 | 6 | public class TokenIssueEventHandlerSetup : IConfigureOptions 7 | where TEventHandler : ITokenIssueEventHandler 8 | { 9 | private readonly ITokenIssueEventHandler _eventHandler; 10 | 11 | public TokenIssueEventHandlerSetup(TEventHandler eventHandler) 12 | { 13 | if (eventHandler == null) 14 | throw new ArgumentNullException(nameof(eventHandler)); 15 | 16 | _eventHandler = eventHandler; 17 | } 18 | 19 | public void Configure(TokensIssuingOptions options) 20 | { 21 | options.WithTokenIssueEventHandler(_eventHandler); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/ITokenIssueEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer 2 | { 3 | using System; 4 | using System.Security.Claims; 5 | 6 | public interface ITokenIssueEventHandler 7 | { 8 | void IssueSuccessEventHandle(string token, Claim[] user); 9 | void LoginNotFoundEventHandle(string login); 10 | void IncorrectPasswordEventHandle(string login, string password); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Models/TokenErrorResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Models 2 | { 3 | public class TokenErrorResponseModel 4 | { 5 | public string ErrorMessage { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Models/TokenRequestModel.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Models 2 | { 3 | public class TokenRequestModel 4 | { 5 | public string Login { get; set; } 6 | public string Password { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/Models/TokenResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.Models 2 | { 3 | using System; 4 | 5 | public class TokenResponseModel 6 | { 7 | public string Token { get; set; } 8 | 9 | public DateTime? ExpirationDate { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/TokensIssuingOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer 2 | { 3 | using System; 4 | using Microsoft.IdentityModel.Tokens; 5 | 6 | public class TokensIssuingOptions 7 | { 8 | public string GetEndpoint { get; set; } 9 | 10 | public SecurityKey SigningKey { get; set; } 11 | 12 | public string SigningAlgorithmName { get; set; } 13 | 14 | public TimeSpan? Lifetime { get; set; } 15 | 16 | public ITokenIssueEventHandler TokenIssueEventHandler { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/UserClaimsProvider/Exceptions/IncorrectPasswordException.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.UserClaimsProvider.Exceptions 2 | { 3 | using System; 4 | 5 | public class IncorrectPasswordException : Exception 6 | { 7 | public IncorrectPasswordException(string login) : base($"Password is incorrect for login {login}") 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/UserClaimsProvider/Exceptions/LoginNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.UserClaimsProvider.Exceptions 2 | { 3 | using System; 4 | 5 | public class LoginNotFoundException : Exception 6 | { 7 | public LoginNotFoundException(string login) : base($"Login {login} wasn't found") 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/JwtBearer/UserClaimsProvider/IUserClaimsProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Authentication.JwtBearer.UserClaimsProvider 2 | { 3 | using System.Security.Claims; 4 | using System.Threading.Tasks; 5 | 6 | public interface IUserClaimsProvider 7 | { 8 | Task GetClaimsAsync(string login, string password); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Authentication/Web.Authentication.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with authentication models implementations 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Authentication 13 | Skeleton.Web.Authentication 14 | Skeleton.Web.Authentication 15 | WebApi authentication 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Conventions/Responses/ApiExceptionResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Conventions.Responses 2 | { 3 | using System; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | 7 | [DataContract] 8 | public class ApiExceptionResponse 9 | { 10 | [DataMember(EmitDefaultValue = false, IsRequired = false, Order = 1)] 11 | public string Message { get; set; } 12 | 13 | [DataMember(EmitDefaultValue = false, IsRequired = false, Order = 2)] 14 | public ExceptionDescription ExceptionDescription { get; set; } 15 | 16 | public ApiExceptionResponse() 17 | { 18 | } 19 | 20 | public ApiExceptionResponse(string message, Exception exception) : this() 21 | { 22 | Message = message; 23 | if(exception != null) 24 | ExceptionDescription = new ExceptionDescription(exception); 25 | } 26 | 27 | public override string ToString() 28 | { 29 | var builder = new StringBuilder(); 30 | 31 | if (string.IsNullOrWhiteSpace(Message) == false) 32 | builder.AppendLine(Message); 33 | 34 | var exceptionDescriptionString = ExceptionDescription?.ToString(); 35 | if (string.IsNullOrWhiteSpace(exceptionDescriptionString) == false) 36 | builder.AppendLine(exceptionDescriptionString); 37 | 38 | return builder.ToString(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Conventions/Responses/ApiResponseError.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Conventions.Responses 2 | { 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | 6 | /// 7 | /// Contract for transmission single error what occurs during Web Api request processing 8 | /// 9 | [DataContract] 10 | public class ApiResponseError 11 | { 12 | /// 13 | /// Error code 14 | /// 15 | [DataMember(EmitDefaultValue = false, IsRequired = false, Order = 1)] 16 | public string Code { get; set; } 17 | 18 | /// 19 | /// Short error description 20 | /// 21 | [DataMember(EmitDefaultValue = false, IsRequired = false, Order = 2)] 22 | public string Title { get; set; } 23 | 24 | /// 25 | /// Full error description 26 | /// 27 | [DataMember(EmitDefaultValue = false, IsRequired = false, Order = 3)] 28 | public string Detail { get; set; } 29 | 30 | public override string ToString() 31 | { 32 | var builder = new StringBuilder(); 33 | 34 | if (string.IsNullOrWhiteSpace(Code) == false) 35 | builder.Append($"{nameof(Code)}: {Code}; "); 36 | if (string.IsNullOrWhiteSpace(Title) == false) 37 | builder.Append($"{nameof(Title)}: {Title}; "); 38 | if (string.IsNullOrWhiteSpace(Detail) == false) 39 | builder.Append($"{nameof(Detail)}: {Detail}; "); 40 | 41 | return builder.ToString(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Conventions/Web.Conventions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with common conventions for web applications development 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard1.1 12 | Skeleton.Web.Conventions 13 | Skeleton.Web.Conventions 14 | Skeleton.Web.Conventions 15 | Web conventions 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Hosting/HostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Hosting 2 | { 3 | using System.IO; 4 | using Autofac.Extensions.DependencyInjection; 5 | using Configuration; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.Hosting; 9 | using Serilog; 10 | using Skeleton.Web.Logging.Serilog.Configuration; 11 | 12 | public static class HostBuilderExtensions 13 | { 14 | public static IHostBuilder CreateDefaultWebApiHostBuilder(string[] args) where TStartup : WebApiBaseStartup => 15 | new HostBuilder() 16 | .UseServiceProviderFactory(new AutofacServiceProviderFactory()) 17 | .UseContentRoot(Directory.GetCurrentDirectory()) 18 | .ConfigureHostConfiguration(config => config.AddEnvironmentVariables("DOTNET_").AddCommandLine(args)) 19 | .ConfigureAppConfiguration( 20 | (context, builder) => 21 | { 22 | var env = context.HostingEnvironment; 23 | 24 | builder 25 | .AddDefaultConfigs(env) 26 | .AddCommandLine(args); 27 | } 28 | ) 29 | .UseSerilog((context, configuration) => configuration.UseDefaultSettings(context.Configuration)) 30 | .ConfigureWebHost( 31 | webHostBuilder => webHostBuilder 32 | .UseKestrel(x => x.AllowSynchronousIO = true) 33 | .UseIISIntegration() 34 | .UseStartup() 35 | ); 36 | } 37 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Hosting/Web.Hosting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with common classes for Web Applications hosting 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Hosting 13 | Skeleton.Web.Hosting 14 | Skeleton.Web.Hosting 15 | Web Hosting 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Configuration/BaseClientOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Configuration 2 | { 3 | using System; 4 | using Serialization.Abstractions; 5 | 6 | public class BaseClientOptions 7 | { 8 | public string BaseUrl { get; set; } 9 | 10 | public TimeSpan Timeout { get; set; } 11 | 12 | public ISerializer Serializer { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Configuration/HttpClientBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Configuration 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Options; 8 | 9 | public static class HttpClientBuilderExtensions 10 | { 11 | public static IHttpClientBuilder ConfigureClient( 12 | this IHttpClientBuilder httpClientBuilder, 13 | IConfiguration config, 14 | Action> optionsConfigurator) 15 | where TClientOptions : BaseClientOptions 16 | { 17 | if(httpClientBuilder == null) 18 | throw new ArgumentNullException(nameof(httpClientBuilder)); 19 | if (config == null) 20 | throw new ArgumentNullException(nameof(config)); 21 | if (optionsConfigurator == null) 22 | throw new ArgumentNullException(nameof(optionsConfigurator)); 23 | 24 | var services = httpClientBuilder.Services; 25 | services.Configure(config); 26 | optionsConfigurator(services.AddOptions()); 27 | 28 | return httpClientBuilder; 29 | } 30 | 31 | public static IHttpClientBuilder UseDefaultPrimaryMessageHandler( 32 | this IHttpClientBuilder httpClientBuilder, 33 | Func handlerConfigurator) 34 | { 35 | if (httpClientBuilder == null) 36 | throw new ArgumentNullException(nameof(httpClientBuilder)); 37 | 38 | return httpClientBuilder.ConfigurePrimaryHttpMessageHandler( 39 | x => handlerConfigurator(new HttpClientHandler()) 40 | ); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Configuration/HttpClientHandlerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Configuration 2 | { 3 | using System; 4 | using System.Net; 5 | using System.Net.Http; 6 | 7 | public static class HttpClientHandlerExtensions 8 | { 9 | public static HttpClientHandler WithAutomaticDecompression(this HttpClientHandler handler) 10 | { 11 | if(handler == null) 12 | throw new ArgumentNullException(nameof(handler)); 13 | 14 | handler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; 15 | return handler; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Configuration/OptionsBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Configuration 2 | { 3 | using System; 4 | using Microsoft.Extensions.Options; 5 | using Serialization.Abstractions; 6 | 7 | public static class OptionsBuilderExtensions 8 | { 9 | public static OptionsBuilder WithSerializer( 10 | this OptionsBuilder optionsBuilder, 11 | ISerializer serializer) 12 | where TClientOptions : BaseClientOptions 13 | { 14 | if(optionsBuilder == null) 15 | throw new ArgumentNullException(nameof(optionsBuilder)); 16 | if (serializer == null) 17 | throw new ArgumentNullException(nameof(serializer)); 18 | 19 | return optionsBuilder.Configure(x => x.Serializer = serializer); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Configuration/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Configuration 2 | { 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | public static class ServiceCollectionExtensions 6 | { 7 | public static IHttpClientBuilder AddClient(this IServiceCollection services) 8 | where TImplementation : BaseClient 9 | { 10 | return services.AddHttpClient(); 11 | } 12 | 13 | public static IHttpClientBuilder AddClient(this IServiceCollection services) 14 | where TContract : class 15 | where TImplementation : BaseClient, TContract 16 | { 17 | return services.AddHttpClient(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Exceptions/ApiException.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Exceptions 2 | { 3 | using System; 4 | using System.Net; 5 | 6 | public class ApiException : Exception 7 | { 8 | public HttpStatusCode StatusCode { get; } 9 | 10 | public ApiException(HttpStatusCode statusCode, string message) : base(message) 11 | { 12 | StatusCode = statusCode; 13 | } 14 | 15 | public ApiException(HttpStatusCode statusCode, string message, Exception innerException) : base(message, innerException) 16 | { 17 | StatusCode = statusCode; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Exceptions/BadRequestException.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Exceptions 2 | { 3 | using System; 4 | 5 | public class BadRequestException : Exception 6 | { 7 | public BadRequestException(string message) : base(message) 8 | { 9 | } 10 | 11 | public BadRequestException(string message, Exception innerException) : base(message, innerException) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Exceptions 2 | { 3 | using System; 4 | 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string message) : base(message) 8 | { 9 | } 10 | 11 | public NotFoundException(string message, Exception innerException) : base(message, innerException) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/Exceptions/UnauthorizedException.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient.Exceptions 2 | { 3 | using System; 4 | 5 | public class UnauthorizedException : Exception 6 | { 7 | public UnauthorizedException(string message) : base(message) 8 | { 9 | } 10 | 11 | public UnauthorizedException(string message, Exception innerException) : base(message, innerException) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiClient/RequestHeadersExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiClient 2 | { 3 | using System; 4 | using System.Net.Http.Headers; 5 | using System.Text; 6 | 7 | public static class HttpRequestHeadersExtensions 8 | { 9 | public static HttpRequestHeaders WithBearerToken(this HttpRequestHeaders headers, string token) 10 | { 11 | if (headers == null) 12 | throw new ArgumentNullException(nameof(headers)); 13 | if (string.IsNullOrWhiteSpace(token)) 14 | throw new ArgumentNullException(nameof(token)); 15 | 16 | headers.Authorization = new AuthenticationHeaderValue("Bearer", token); 17 | return headers; 18 | } 19 | 20 | public static HttpRequestHeaders WithBasicAuth(this HttpRequestHeaders headers, string login, string password) 21 | { 22 | if (headers == null) 23 | throw new ArgumentNullException(nameof(headers)); 24 | if (string.IsNullOrWhiteSpace(login)) 25 | throw new ArgumentNullException(nameof(login)); 26 | if (string.IsNullOrWhiteSpace(password)) 27 | throw new ArgumentNullException(nameof(password)); 28 | 29 | var value = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{login}:{password}")); 30 | headers.Authorization = new AuthenticationHeaderValue("Basic", value); 31 | return headers; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/BaseApiFluentClient/BaseFluentClient.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Integration.BaseApiFluentClient 2 | { 3 | using System.Collections.Generic; 4 | using BaseApiClient; 5 | 6 | public abstract class BaseFluentClient 7 | { 8 | protected readonly List ServicesClients; 9 | public dynamic CurrentState { get; protected set; } 10 | 11 | protected BaseFluentClient() 12 | { 13 | ServicesClients = new List(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Integration/Web.Integration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library contains base client for integration with WebApi 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard2.0 12 | Skeleton.Web.Integration 13 | Skeleton.Web.Integration 14 | Skeleton.Web.Integration 15 | Integrations;WebApi client 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Abstractions/ContextsCapturing/CapturedData/CapturedActionExecutingContext.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.Abstractions.ContextsCapturing.CapturedData 2 | { 3 | using System.Collections.Generic; 4 | 5 | internal class CapturedActionExecutingContext 6 | { 7 | public string Method { get; set; } 8 | 9 | public string Url { get; set; } 10 | 11 | public IDictionary Headers { get; set; } 12 | 13 | public IDictionary ActionArguments { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Abstractions/ContextsCapturing/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.Abstractions.ContextsCapturing 2 | { 3 | using System; 4 | using System.Linq; 5 | using CapturedData; 6 | using Microsoft.AspNetCore.Http.Extensions; 7 | using Microsoft.AspNetCore.Mvc.Filters; 8 | using Microsoft.Extensions.Logging; 9 | 10 | public static class LoggerExtensions 11 | { 12 | public static IDisposable CaptureActionExecutingContext(this ILogger logger, ActionExecutingContext context) 13 | { 14 | var capturedContext = new CapturedActionExecutingContext(); 15 | 16 | if (context.HttpContext?.Request != null) 17 | { 18 | var request = context.HttpContext.Request; 19 | 20 | capturedContext.Method = request.Method; 21 | capturedContext.Url = request.GetDisplayUrl(); 22 | capturedContext.Headers = request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()); 23 | } 24 | if (context.ActionArguments != null && context.ActionArguments.Count > 0) 25 | capturedContext.ActionArguments = context.ActionArguments; 26 | 27 | return logger.BeginScope("{@Context}", capturedContext); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Abstractions/Web.Logging.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Common classes for logging in web application 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Logging.Abstractions 13 | Skeleton.Web.Logging.Abstractions 14 | Skeleton.Web.Logging.Abstractions 15 | Structured logging 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.NLog/ConfigurationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.NLog 2 | { 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.IO; 5 | using global::NLog; 6 | using global::NLog.Config; 7 | using Microsoft.Extensions.Configuration; 8 | 9 | public static class ConfigurationBuilderExtensions 10 | { 11 | [ExcludeFromCodeCoverage] 12 | public static IConfigurationBuilder AddNLogConfig(this IConfigurationBuilder configurationBuilder, string relativeConfigPath) 13 | { 14 | var fullConfigPath = Path.Combine(configurationBuilder.GetFileProvider().GetFileInfo(relativeConfigPath).PhysicalPath); 15 | LogManager.Configuration = new XmlLoggingConfiguration(fullConfigPath, false); 16 | return configurationBuilder; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.NLog/LoggingBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.NLog 2 | { 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Reflection; 5 | using global::NLog; 6 | using global::NLog.Extensions.Logging; 7 | using Microsoft.Extensions.Logging; 8 | 9 | public static class LoggingBuilderExtensions 10 | { 11 | [ExcludeFromCodeCoverage] 12 | public static ILoggingBuilder AddNLog(this ILoggingBuilder loggingBuilder) 13 | { 14 | LogManager.AddHiddenAssembly(Assembly.Load(new AssemblyName("Microsoft.Extensions.Logging"))); 15 | LogManager.AddHiddenAssembly(Assembly.Load(new AssemblyName("Microsoft.Extensions.Logging.Abstractions"))); 16 | LogManager.AddHiddenAssembly(typeof(ConfigureExtensions).GetTypeInfo().Assembly); 17 | LogManager.AddHiddenAssembly(typeof(LoggingBuilderExtensions).GetTypeInfo().Assembly); 18 | 19 | loggingBuilder.AddProvider(new NLogLoggerProvider()); 20 | return loggingBuilder; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.NLog/Web.Logging.NLog.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with classe for NLog integration in ASP.NET Core application 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard2.0 12 | Skeleton.Web.Logging.NLog 13 | Skeleton.Web.Logging.NLog 14 | Skeleton.Web.Logging.NLog 15 | NLog AspNetCore 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Serilog/Configuration/LoggerConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.Serilog.Configuration 2 | { 3 | using System; 4 | using Enrichers; 5 | using global::Serilog; 6 | using global::Serilog.Exceptions; 7 | using Microsoft.Extensions.Configuration; 8 | 9 | public static class LoggerConfigurationExtensions 10 | { 11 | public static LoggerConfiguration UseDefaultSettings( 12 | this LoggerConfiguration loggerConfiguration, 13 | IConfiguration configuration) 14 | { 15 | if (loggerConfiguration == null) 16 | throw new ArgumentNullException(nameof(loggerConfiguration)); 17 | if (configuration == null) 18 | throw new ArgumentNullException(nameof(configuration)); 19 | 20 | return loggerConfiguration 21 | .Enrich.WithApplicationInformationalVersion() 22 | .Enrich.WithExceptionDetails() 23 | .Enrich.WithMessageTemplateHash() 24 | .Enrich.WithLogEventHash() 25 | .Enrich.FromLogContext() 26 | .ReadFrom.Configuration(configuration); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Serilog/Enrichers/LogEventHashEnricher.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.Serilog.Enrichers 2 | { 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Text; 6 | using global::Serilog.Core; 7 | using global::Serilog.Events; 8 | using Murmur; 9 | 10 | [ExcludeFromCodeCoverage] 11 | public class LogEventHashEnricher : ILogEventEnricher 12 | { 13 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 14 | { 15 | var builder = new StringBuilder(logEvent.MessageTemplate.Text); 16 | for (var exception = logEvent.Exception; exception != null; exception = exception.InnerException) 17 | { 18 | builder.AppendLine(exception.GetType().AssemblyQualifiedName); 19 | builder.AppendLine(exception.StackTrace); 20 | } 21 | var bytes = Encoding.UTF8.GetBytes(builder.ToString()); 22 | 23 | var hash = MurmurHash.Create32().ComputeHash(bytes); 24 | var numericHash = BitConverter.ToUInt32(hash, 0); 25 | 26 | logEvent.AddPropertyIfAbsent( 27 | propertyFactory.CreateProperty("LogEventHash", numericHash.ToString("x8")) 28 | ); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Serilog/Enrichers/MessageTemplateHashEnricher.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.Serilog.Enrichers 2 | { 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Text; 6 | using global::Serilog.Core; 7 | using global::Serilog.Events; 8 | using Murmur; 9 | 10 | [ExcludeFromCodeCoverage] 11 | public class MessageTemplateHashEnricher : ILogEventEnricher 12 | { 13 | public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) 14 | { 15 | var bytes = Encoding.UTF8.GetBytes(logEvent.MessageTemplate.Text); 16 | 17 | var hash = MurmurHash.Create32().ComputeHash(bytes); 18 | var numericHash = BitConverter.ToUInt32(hash, 0); 19 | 20 | logEvent.AddPropertyIfAbsent( 21 | propertyFactory.CreateProperty("MessageTemplateHash", numericHash.ToString("x8")) 22 | ); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Serilog/Formatting/RenderedJsonFormatter.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Logging.Serilog.Formatting 2 | { 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using global::Serilog.Formatting.Json; 6 | 7 | [ExcludeFromCodeCoverage] 8 | public class RenderedJsonFormatter : JsonFormatter 9 | { 10 | public RenderedJsonFormatter(string closingDelimiter = null, IFormatProvider formatProvider = null) : 11 | base(closingDelimiter, true, formatProvider) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Logging.Serilog/Web.Logging.Serilog.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with classe for Serilog integration in ASP.NET Core application 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard2.0 12 | Skeleton.Web.Logging.Serilog 13 | Skeleton.Web.Logging.Serilog 14 | Skeleton.Web.Logging.Serilog 15 | Serilog AspNetCore 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Abstractions/ISerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Abstractions 2 | { 3 | using System.IO; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | 7 | public interface ISerializer 8 | { 9 | HttpContent Serialize(object obj); 10 | 11 | T Deserialize(Stream stream); 12 | 13 | MediaTypeHeaderValue MediaType { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Abstractions/Web.Serialization.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with abstractions for content serialization in Web API 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netstandard1.1 12 | Skeleton.Web.Serialization.Abstractions 13 | Skeleton.Web.Serialization.Abstractions 14 | Skeleton.Web.Serialization.Abstractions 15 | Serialization 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Jil/Configuration/MvcBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Jil.Configuration 2 | { 3 | using System; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.DependencyInjection.Extensions; 7 | using Microsoft.Extensions.Options; 8 | 9 | public static class MvcBuilderExtensions 10 | { 11 | public static IMvcBuilder WithJsonFormattersBasedOnJil( 12 | this IMvcBuilder builder, 13 | global::Jil.Options serializerSettings) 14 | { 15 | if(builder == null) 16 | throw new ArgumentNullException(nameof(builder)); 17 | 18 | 19 | builder.Services.AddSingleton(Options.Create(new MvcJilOptions {SerializerSettings = serializerSettings})); 20 | builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, MvcOptionsSetup>()); 21 | return builder; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Jil/Configuration/MvcJilOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Jil.Configuration 2 | { 3 | using global::Jil; 4 | 5 | public class MvcJilOptions 6 | { 7 | public Options SerializerSettings { get; internal set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Jil/Configuration/MvcOptionsSetup.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Jil.Configuration 2 | { 3 | using System; 4 | using Formatters; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.Extensions.Logging; 8 | using Microsoft.Extensions.Options; 9 | 10 | public class MvcOptionsSetup : IConfigureOptions 11 | { 12 | private readonly ILoggerFactory _loggerFactory; 13 | private readonly global::Jil.Options _serializerSettings; 14 | 15 | public MvcOptionsSetup(ILoggerFactory loggerFactory, IOptions jilOptions) 16 | { 17 | if(loggerFactory == null) 18 | throw new ArgumentNullException(nameof(loggerFactory)); 19 | if(jilOptions == null) 20 | throw new ArgumentNullException(nameof(jilOptions)); 21 | 22 | _loggerFactory = loggerFactory; 23 | _serializerSettings = jilOptions.Value.SerializerSettings; 24 | } 25 | 26 | public void Configure(MvcOptions options) 27 | { 28 | options.InputFormatters.RemoveType(); 29 | options.InputFormatters.Add(new JilInputFormatter(_loggerFactory.CreateLogger(), _serializerSettings)); 30 | 31 | options.OutputFormatters.RemoveType(); 32 | options.OutputFormatters.Add(new JilOutputFormatter(_loggerFactory.CreateLogger(), _serializerSettings)); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Jil/Configuration/OptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Jil.Configuration 2 | { 3 | using global::Jil; 4 | 5 | public static class OptionsExtensions 6 | { 7 | public static Options Default => 8 | new Options( 9 | prettyPrint: false, 10 | excludeNulls: true, 11 | jsonp: false, 12 | dateFormat: DateTimeFormat.ISO8601, 13 | includeInherited: true, 14 | unspecifiedDateTimeKindBehavior: UnspecifiedDateTimeKindBehavior.IsUTC, 15 | serializationNameFormat: SerializationNameFormat.CamelCase); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Jil/Formatters/MediaTypeHeaderValues.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Jil.Formatters 2 | { 3 | using Microsoft.Net.Http.Headers; 4 | 5 | public static class MediaTypeHeaderValues 6 | { 7 | public static readonly MediaTypeHeaderValue ApplicationJson = MediaTypeHeaderValue.Parse("application/json").CopyAsReadOnly(); 8 | public static readonly MediaTypeHeaderValue TextJson = MediaTypeHeaderValue.Parse("text/json").CopyAsReadOnly(); 9 | public static readonly MediaTypeHeaderValue ApplicationJsonPatch = MediaTypeHeaderValue.Parse("application/json-patch+json").CopyAsReadOnly(); 10 | public static readonly MediaTypeHeaderValue ApplicationAnyJsonSyntax = MediaTypeHeaderValue.Parse("application/*+json").CopyAsReadOnly(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Jil/Web.Serialization.Jil.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library for Jil serializer integration 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Serialization.Jil 13 | Skeleton.Web.Serialization.Jil 14 | Skeleton.Web.Serialization.Jil 15 | Jil;InputFormatter;OutputFormatter 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.JsonNet/Configuration/MvcBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.JsonNet.Configuration 2 | { 3 | using System; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Newtonsoft.Json; 6 | 7 | public static class MvcBuilderExtensions 8 | { 9 | public static IMvcBuilder WithJsonFormattersBasedOnJsonNet( 10 | this IMvcBuilder builder, 11 | Action settingsConfigurator) 12 | { 13 | if(builder == null) 14 | throw new ArgumentNullException(nameof(builder)); 15 | if (settingsConfigurator == null) 16 | throw new ArgumentNullException(nameof(settingsConfigurator)); 17 | 18 | builder.AddNewtonsoftJson(x => settingsConfigurator(x.SerializerSettings)); 19 | return builder; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.JsonNet/JsonConverters/DateTimeConverter.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.JsonNet.JsonConverters 2 | { 3 | using System; 4 | using System.Globalization; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Converters; 7 | 8 | public class DateTimeConverter : IsoDateTimeConverter 9 | { 10 | public DateTimeConverter() 11 | { 12 | DateTimeStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal; 13 | DateTimeFormat = "o"; 14 | } 15 | 16 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 17 | { 18 | string resultString; 19 | 20 | if (value is DateTime) 21 | { 22 | var dateTime = (DateTime)value; 23 | 24 | if ((DateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal && dateTime.Kind == DateTimeKind.Unspecified) 25 | dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); 26 | if ((DateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal) 27 | dateTime = dateTime.ToUniversalTime(); 28 | 29 | resultString = dateTime.ToString(DateTimeFormat, Culture); 30 | } 31 | else 32 | { 33 | var dateTimeOffset = (DateTimeOffset)value; 34 | resultString = dateTimeOffset.ToString(DateTimeFormat, Culture); 35 | } 36 | 37 | writer.WriteValue(resultString); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.JsonNet/PropertyNamesResolvers/SnakeCasePropertyNamesContractResolver.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.JsonNet.PropertyNamesResolvers 2 | { 3 | using Newtonsoft.Json.Serialization; 4 | 5 | public class SnakeCasePropertyNamesContractResolver : DefaultContractResolver 6 | { 7 | public SnakeCasePropertyNamesContractResolver() 8 | { 9 | NamingStrategy = new SnakeCaseNamingStrategy(true, true); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.JsonNet/Serializer/JsonNetSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.JsonNet.Serializer 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Net.Http; 6 | using System.Net.Http.Headers; 7 | using System.Text; 8 | using Abstractions; 9 | using Configuration; 10 | using Newtonsoft.Json; 11 | 12 | public class JsonNetSerializer : ISerializer 13 | { 14 | private readonly JsonSerializerSettings _settings; 15 | 16 | public JsonNetSerializer(JsonSerializerSettings settings) 17 | { 18 | if(settings == null) 19 | throw new ArgumentNullException(nameof(settings)); 20 | _settings = settings; 21 | 22 | MediaType = MediaType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); 23 | } 24 | 25 | public HttpContent Serialize(object obj) 26 | { 27 | return new StringContent( 28 | JsonConvert.SerializeObject(obj, _settings), 29 | Encoding.GetEncoding(MediaType.CharSet), 30 | MediaType.MediaType 31 | ); 32 | } 33 | 34 | public T Deserialize(Stream stream) 35 | { 36 | using (var streamReader = new StreamReader(stream, Encoding.UTF8, true, 4096, true)) 37 | using (var jsonTextReader = new JsonTextReader(streamReader)) 38 | return JsonSerializer.CreateDefault(_settings).Deserialize(jsonTextReader); 39 | } 40 | 41 | public MediaTypeHeaderValue MediaType { get; } 42 | 43 | public static JsonNetSerializer Default => new JsonNetSerializer(new JsonSerializerSettings().UseDefaultSettings()); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.JsonNet/Web.Serialization.JsonNet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with serialization conventions and settings for Newtonsoft.Json 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Serialization.JsonNet 13 | Skeleton.Web.Serialization.JsonNet 14 | Skeleton.Web.Serialization.JsonNet 15 | JsonNet;Newtonsoft.Json;Conventions 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Configuration/MvcBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Protobuf.Configuration 2 | { 3 | using System; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.DependencyInjection.Extensions; 7 | using Microsoft.Extensions.Options; 8 | using ProtoBuf.Meta; 9 | 10 | public static class MvcBuilderExtensions 11 | { 12 | public static IMvcBuilder WithProtobufFormatters( 13 | this IMvcBuilder builder, 14 | Func serializerConfigurator = null) 15 | { 16 | if (builder == null) 17 | throw new ArgumentNullException(nameof(builder)); 18 | 19 | builder.Services.AddSingleton(Options.Create(new MvcProtobufOptions {SerializerConfigurator = serializerConfigurator ?? (x => x)})); 20 | builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, MvcOptionsSetup>()); 21 | return builder; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Configuration/MvcOptionsSetup.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Protobuf.Configuration 2 | { 3 | using System; 4 | using Formatters; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.Options; 8 | using ProtoBuf.Meta; 9 | 10 | public class MvcOptionsSetup : IConfigureOptions 11 | { 12 | private readonly ILoggerFactory _loggerFactory; 13 | private readonly Func _serializerConfigurator; 14 | 15 | public MvcOptionsSetup(ILoggerFactory loggerFactory, IOptions protobufOptions) 16 | { 17 | if (loggerFactory == null) 18 | throw new ArgumentNullException(nameof(loggerFactory)); 19 | if (protobufOptions == null) 20 | throw new ArgumentNullException(nameof(protobufOptions)); 21 | 22 | _loggerFactory = loggerFactory; 23 | _serializerConfigurator = x => protobufOptions.Value.SerializerConfigurator(x).WithDefaultSettings(); 24 | } 25 | 26 | public void Configure(MvcOptions options) 27 | { 28 | options.InputFormatters.Add( 29 | new ProtobufInputFormatter(_loggerFactory.CreateLogger(), _serializerConfigurator) 30 | ); 31 | options.OutputFormatters.Add( 32 | new ProtobufOutputFormatter(_loggerFactory.CreateLogger(), _serializerConfigurator) 33 | ); 34 | options.FormatterMappings.SetMediaTypeMappingForFormat("protobuf", MediaTypeHeaderValues.ApplicationProtobuf); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Configuration/MvcProtobufOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Protobuf.Configuration 2 | { 3 | using System; 4 | using ProtoBuf.Meta; 5 | 6 | public class MvcProtobufOptions 7 | { 8 | public Func SerializerConfigurator { get; internal set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Formatters/MediaTypeHeaderValues.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Protobuf.Formatters 2 | { 3 | using Microsoft.Net.Http.Headers; 4 | 5 | public static class MediaTypeHeaderValues 6 | { 7 | public static readonly MediaTypeHeaderValue ApplicationProtobuf = MediaTypeHeaderValue.Parse("application/x-protobuf").CopyAsReadOnly(); 8 | public static readonly MediaTypeHeaderValue ApplicationProtobufSynonym = MediaTypeHeaderValue.Parse("application/protobuf").CopyAsReadOnly(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Formatters/Surrogates/DateTimeOffsetSurrogate.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Protobuf.Formatters.Surrogates 2 | { 3 | using System; 4 | using ProtoBuf; 5 | 6 | [ProtoContract] 7 | public class DateTimeOffsetSurrogate 8 | { 9 | [ProtoMember(1)] 10 | public long DateTimeTicks { get; set; } 11 | 12 | [ProtoMember(2)] 13 | public short OffsetMinutes { get; set; } 14 | 15 | public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value) 16 | { 17 | return new DateTimeOffsetSurrogate 18 | { 19 | DateTimeTicks = value.Ticks, 20 | OffsetMinutes = (short)value.Offset.TotalMinutes 21 | }; 22 | } 23 | 24 | public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value) 25 | { 26 | return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes)); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Serializer/ProtobufSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Serialization.Protobuf.Serializer 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Net.Http; 6 | using Abstractions; 7 | using Configuration; 8 | using ProtoBuf.Meta; 9 | using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; 10 | 11 | public class ProtobufSerializer : ISerializer 12 | { 13 | private readonly RuntimeTypeModel _runtimeTypeModel; 14 | 15 | public ProtobufSerializer(Func serializerConfigurator) 16 | { 17 | if (serializerConfigurator == null) 18 | throw new ArgumentNullException(nameof(serializerConfigurator)); 19 | _runtimeTypeModel = serializerConfigurator(RuntimeTypeModel.Create()); 20 | 21 | MediaType = MediaTypeHeaderValue.Parse("application/x-protobuf"); 22 | } 23 | 24 | public HttpContent Serialize(object obj) 25 | { 26 | using (var stream = new MemoryStream()) 27 | { 28 | if (obj != null) 29 | _runtimeTypeModel.Serialize(stream, obj); 30 | return new ByteArrayContent(stream.ToArray()) {Headers = {ContentType = MediaType}}; 31 | } 32 | } 33 | 34 | public T Deserialize(Stream stream) 35 | { 36 | return (T)_runtimeTypeModel.Deserialize(stream, null, typeof(T)); 37 | } 38 | 39 | public MediaTypeHeaderValue MediaType { get; } 40 | 41 | public static ProtobufSerializer Default => new ProtobufSerializer(x => x.WithDefaultSettings()); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Serialization.Protobuf/Web.Serialization.Protobuf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library for protobuf-net serializer integration 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Serialization.Protobuf 13 | Skeleton.Web.Serialization.Protobuf 14 | Skeleton.Web.Serialization.Protobuf 15 | protobuf-net;InputFormatter;OutputFormatter 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Infrastructure/Web.Testing/BaseApiClientTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Testing 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | using Hosting; 6 | using Integration.BaseApiClient.Configuration; 7 | using Integration.BaseApiFluentClient; 8 | using Microsoft.Extensions.Options; 9 | using Serialization.Jil.Serializer; 10 | 11 | public class BaseApiClientTests where TStartup : WebApiBaseStartup 12 | { 13 | protected readonly BaseApiTestsFixture Fixture; 14 | 15 | protected BaseApiClientTests(BaseApiTestsFixture fixture) 16 | { 17 | Fixture = fixture; 18 | Fixture.MockLogger.Invocations.Clear(); 19 | } 20 | 21 | protected TClient CreateClient(Action configureOptions = null) 22 | where TClient : BaseFluentClient 23 | where TClientOptions : BaseClientOptions, new() 24 | { 25 | var clientOptions 26 | = new TClientOptions 27 | { 28 | BaseUrl = Fixture.ClientOptions.BaseAddress.ToString(), 29 | Timeout = Fixture.ApiTimeout, 30 | Serializer = JilSerializer.Default 31 | }; 32 | configureOptions?.Invoke(clientOptions); 33 | 34 | return (TClient)Activator.CreateInstance( 35 | typeof(TClient), 36 | Fixture.CreateClient(), 37 | Options.Create(clientOptions) 38 | ); 39 | } 40 | 41 | protected dynamic CreateAsyncClient(TClient client) where TClient : BaseFluentClient => 42 | new FluentChainedTask(Task.FromResult(client)); 43 | } 44 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Testing/BaseServiceClientTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Testing 2 | { 3 | using System; 4 | using Hosting; 5 | using Integration.BaseApiClient; 6 | using Integration.BaseApiClient.Configuration; 7 | using Microsoft.Extensions.Options; 8 | using Serialization.Jil.Serializer; 9 | 10 | public class BaseServiceClientTests where TStartup : WebApiBaseStartup 11 | { 12 | protected readonly BaseApiTestsFixture Fixture; 13 | 14 | protected BaseServiceClientTests(BaseApiTestsFixture fixture) 15 | { 16 | Fixture = fixture; 17 | Fixture.MockLogger.Invocations.Clear(); 18 | } 19 | 20 | protected TClient CreateClient(Action configureOptions = null) 21 | where TClient : BaseClient 22 | where TClientOptions : BaseClientOptions, new() 23 | { 24 | var clientOptions 25 | = new TClientOptions 26 | { 27 | BaseUrl = Fixture.ClientOptions.BaseAddress.ToString(), 28 | Timeout = Fixture.ApiTimeout, 29 | Serializer = JilSerializer.Default 30 | }; 31 | configureOptions?.Invoke(clientOptions); 32 | 33 | return (TClient) Activator.CreateInstance( 34 | typeof(TClient), 35 | Fixture.CreateClient(), 36 | Options.Create(clientOptions) 37 | ); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Testing/CustomAutofacServiceProviderFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Testing 2 | { 3 | using System; 4 | using Autofac; 5 | using Autofac.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | public class CustomAutofacServiceProviderFactory : IServiceProviderFactory 9 | { 10 | private readonly Action _overrideRegisteredDependencies; 11 | 12 | public CustomAutofacServiceProviderFactory(Action overrideRegisteredDependencies) 13 | { 14 | if (overrideRegisteredDependencies == null) 15 | throw new ArgumentNullException(nameof(overrideRegisteredDependencies)); 16 | 17 | _overrideRegisteredDependencies = overrideRegisteredDependencies; 18 | } 19 | 20 | public ContainerBuilder CreateBuilder(IServiceCollection services) 21 | { 22 | if (services == null) 23 | throw new ArgumentNullException(nameof(services)); 24 | 25 | var builder = new ContainerBuilder(); 26 | builder.Populate(services); 27 | return builder; 28 | } 29 | 30 | public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) 31 | { 32 | if (containerBuilder == null) 33 | throw new ArgumentNullException(nameof(containerBuilder)); 34 | 35 | _overrideRegisteredDependencies(containerBuilder); 36 | var container = containerBuilder.Build(); 37 | return new AutofacServiceProvider(container); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Testing/Extensions/ConfigurationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Testing.Extensions 2 | { 3 | using System; 4 | using System.Linq; 5 | using Microsoft.Extensions.Configuration; 6 | 7 | public static class ConfigurationBuilderExtensions 8 | { 9 | private static readonly string[] KnownCiNames; 10 | 11 | static ConfigurationBuilderExtensions() 12 | { 13 | KnownCiNames = new[] 14 | { 15 | "Appveyor", 16 | "Travis" 17 | }; 18 | } 19 | 20 | public static IConfigurationBuilder AddCiDependentSettings(this IConfigurationBuilder configurationBuilder, string environment) 21 | { 22 | var ciName = KnownCiNames.FirstOrDefault(x => Environment.GetEnvironmentVariable(x.ToUpper())?.ToUpperInvariant() == "TRUE"); 23 | configurationBuilder.AddJsonFile($"appsettings.{environment}.{ciName ?? ""}.json", true, false); 24 | 25 | if(Environment.GetEnvironmentVariable("CI_WINDOWS")?.ToUpperInvariant() == "TRUE") 26 | configurationBuilder.AddJsonFile($"appsettings.{environment}.{ciName ?? ""}.Windows.json", true, false); 27 | 28 | if (Environment.GetEnvironmentVariable("CI_LINUX")?.ToUpperInvariant() == "TRUE") 29 | configurationBuilder.AddJsonFile($"appsettings.{environment}.{ciName ?? ""}.Linux.json", true, false); 30 | 31 | return configurationBuilder; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Testing/Extensions/WebHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Testing.Extensions 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.DependencyInjection.Extensions; 6 | using Microsoft.Extensions.Logging; 7 | using Moq; 8 | 9 | public static class WebHostBuilderExtensions 10 | { 11 | public static IWebHostBuilder UseMockLogger(this IWebHostBuilder builder, Mock mockLogger) 12 | { 13 | var mockLoggerFactory = new Mock(); 14 | mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny())).Returns(mockLogger.Object); 15 | 16 | return builder.ConfigureLogging(x => x.Services.Replace(ServiceDescriptor.Singleton(mockLoggerFactory.Object))); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web.Testing/Web.Testing.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with common classes for creation WebApi integration tests 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web.Testing 13 | Skeleton.Web.Testing 14 | Skeleton.Web.Testing 15 | WebApi integration testing 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Infrastructure/Web/Configuration/ConfigurationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Configuration 2 | { 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public static class ConfigurationBuilderExtensions 7 | { 8 | public static IConfigurationBuilder AddDefaultConfigs( 9 | this IConfigurationBuilder configurationBuilder, 10 | string configsPath, 11 | string environmentName) 12 | { 13 | return configurationBuilder 14 | .AddEnvironmentVariables() 15 | .SetBasePath(configsPath) 16 | .AddJsonFile("appsettings.json", false, true) 17 | .AddJsonFile($"appsettings.{environmentName}.json", false, true); 18 | } 19 | 20 | public static IConfigurationBuilder AddDefaultConfigs( 21 | this IConfigurationBuilder configurationBuilder, 22 | IHostEnvironment hostEnvironment) 23 | { 24 | return configurationBuilder.AddDefaultConfigs(hostEnvironment.ContentRootPath, hostEnvironment.EnvironmentName); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/Documentation/AuthResponsesOperationFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Documentation 2 | { 3 | using System.Linq; 4 | using Common.Extensions; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.OpenApi.Models; 7 | using Swashbuckle.AspNetCore.Swagger; 8 | using Swashbuckle.AspNetCore.SwaggerGen; 9 | 10 | public class AuthResponsesOperationFilter : IOperationFilter 11 | { 12 | public void Apply(OpenApiOperation operation, OperationFilterContext context) 13 | { 14 | var authAttributes = context?.MethodInfo?.DeclaringType?.GetCustomAttributes(true) 15 | .Union(context.MethodInfo?.GetCustomAttributes(true)) 16 | .OfType(); 17 | 18 | if (authAttributes.IsNotEmpty()) 19 | operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Infrastructure/Web/ExceptionsHandling/ApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.ExceptionsHandling 2 | { 3 | using System; 4 | using Microsoft.AspNetCore.Builder; 5 | 6 | public static class ApplicationBuilderExtensions 7 | { 8 | public static IApplicationBuilder UseUnhandledExceptionsLoggingMiddleware(this IApplicationBuilder applicationBuilder) 9 | { 10 | if(applicationBuilder == null) 11 | throw new ArgumentNullException(nameof(applicationBuilder)); 12 | 13 | return applicationBuilder.UseMiddleware(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/ExceptionsHandling/MvcOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.ExceptionsHandling 2 | { 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | public static class MvcOptionsExtensions 6 | { 7 | public static MvcOptions UseUnhandledExceptionFilter(this MvcOptions mvcOptions) 8 | { 9 | mvcOptions.Filters.Add(typeof(UnhandledExceptionsFilterAttribute)); 10 | return mvcOptions; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/ExceptionsHandling/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.ExceptionsHandling 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddUnhandledExceptionsStartupFilter(this IServiceCollection services) 9 | { 10 | return services.AddTransient(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/ExceptionsHandling/UnhandledExceptionsStartupFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.ExceptionsHandling 2 | { 3 | using System; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | 7 | public class UnhandledExceptionsStartupFilter : IStartupFilter 8 | { 9 | public Action Configure(Action next) 10 | { 11 | return builder => 12 | { 13 | next(builder.UseUnhandledExceptionsLoggingMiddleware()); 14 | }; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/Routing/CentralPrefixConvention.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Routing 2 | { 3 | using Microsoft.AspNetCore.Mvc.ApplicationModels; 4 | using Microsoft.AspNetCore.Mvc.Routing; 5 | 6 | public class CentralPrefixConvention : IApplicationModelConvention 7 | { 8 | private readonly AttributeRouteModel _centralRoutePrefix; 9 | 10 | public CentralPrefixConvention(IRouteTemplateProvider routeTemplateProvider) 11 | { 12 | _centralRoutePrefix = new AttributeRouteModel(routeTemplateProvider); 13 | } 14 | 15 | public void Apply(ApplicationModel application) 16 | { 17 | foreach (var controller in application.Controllers) 18 | { 19 | foreach (var selector in controller.Selectors) 20 | { 21 | if (selector.AttributeRouteModel != null) 22 | selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(selector.AttributeRouteModel, 23 | _centralRoutePrefix); 24 | else 25 | selector.AttributeRouteModel = _centralRoutePrefix; 26 | } 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/Routing/MvcOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Routing 2 | { 3 | using System; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.Routing; 6 | 7 | public static class MvcOptionsExtensions 8 | { 9 | public static MvcOptions UseCentralRoutePrefix(this MvcOptions mvcOptions, IRouteTemplateProvider routeTemplateProvider) 10 | { 11 | if(mvcOptions == null) 12 | throw new ArgumentNullException(nameof(mvcOptions)); 13 | if(routeTemplateProvider == null) 14 | throw new ArgumentNullException(nameof(routeTemplateProvider)); 15 | 16 | mvcOptions.Conventions.Insert(0, new CentralPrefixConvention(routeTemplateProvider)); 17 | return mvcOptions; 18 | } 19 | 20 | public static MvcOptions UseCentralRoutePrefix(this MvcOptions mvcOptions, string routeTemplate) 21 | { 22 | return UseCentralRoutePrefix(mvcOptions, new RouteAttribute(routeTemplate)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/Validation/ModelValidationFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Validation 2 | { 3 | using System.Linq; 4 | using Conventions.Responses; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Filters; 7 | 8 | public class ModelValidationFilterAttribute : ActionFilterAttribute 9 | { 10 | public override void OnActionExecuting(ActionExecutingContext context) 11 | { 12 | if (context.ModelState.IsValid) 13 | { 14 | base.OnActionExecuting(context); 15 | return; 16 | } 17 | 18 | var modelErrors = context.ModelState.Values 19 | .SelectMany(x => x.Errors) 20 | .Select(x => new ApiResponseError {Title = x.ErrorMessage, Detail = x.Exception?.Message}) 21 | .ToArray(); 22 | context.Result = new BadRequestObjectResult(ApiResponse.Error(modelErrors)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/Validation/MvcOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Validation 2 | { 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | public static class MvcOptionsExtensions 6 | { 7 | public static MvcOptions UseModelValidationFilter(this MvcOptions mvcOptions) 8 | { 9 | mvcOptions.Filters.Add(); 10 | return mvcOptions; 11 | } 12 | 13 | public static MvcOptions UseParametersValidationFilter(this MvcOptions mvcOptions) 14 | { 15 | mvcOptions.Filters.Add(new ParametersValidationFilterAttribute()); 16 | return mvcOptions; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Infrastructure/Web/Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library with common classes for WebApi creation 5 | 6 | $(Description) 7 | Branch: $(RepositoryBranch) 8 | Working tree: $(RepositoryUrl)/tree/$(RepositoryCommit) 9 | Commit message: $(RepositoryCommitMessage) 10 | 11 | netcoreapp3.1 12 | Skeleton.Web 13 | Skeleton.Web 14 | Skeleton.Web 15 | WebApi common classes 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Web.Application/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Application.Controllers 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | /// 10 | /// Endpoint for account information 11 | /// 12 | public class AccountController : Controller 13 | { 14 | /// 15 | /// Get all claims for current user 16 | /// 17 | /// 18 | [Authorize] 19 | [HttpPost("UserInfo")] 20 | public IEnumerable UserInfo() 21 | { 22 | var identity = (ClaimsIdentity)User.Identity; 23 | 24 | return identity.Claims 25 | .Select(x => $"{x.Type}:{x.Value}") 26 | .ToArray(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Web.Application/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Web.Application")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("5f5403fc-219b-4d72-af7e-2bf4e26f9ec4")] 20 | -------------------------------------------------------------------------------- /src/Web.Application/Services/TokenIssueEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Application.Services 2 | { 3 | using System.Security.Claims; 4 | using Microsoft.Extensions.Logging; 5 | using Skeleton.Web.Authentication.JwtBearer; 6 | 7 | public class TokenIssueEventHandler : ITokenIssueEventHandler 8 | { 9 | readonly ILogger _logger; 10 | 11 | public TokenIssueEventHandler(ILogger logger) 12 | { 13 | _logger = logger; 14 | } 15 | 16 | public void IssueSuccessEventHandle(string token, Claim[] user) 17 | { 18 | _logger.LogDebug("Token issued successfully. Token: {0}", token); 19 | } 20 | 21 | public void LoginNotFoundEventHandle(string login) 22 | { 23 | _logger.LogDebug("Token not issued. Login {0} not found.", login); 24 | } 25 | 26 | public void IncorrectPasswordEventHandle(string login, string password) 27 | { 28 | _logger.LogDebug("Token not issued. Incorrect password {0} for login {1}.", password, login); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Web.Application/Services/UserClaimsProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Application.Services 2 | { 3 | using System.Security.Claims; 4 | using System.Threading.Tasks; 5 | using JetBrains.Annotations; 6 | using Skeleton.Web.Authentication.JwtBearer.UserClaimsProvider; 7 | using Skeleton.Web.Authentication.JwtBearer.UserClaimsProvider.Exceptions; 8 | 9 | [UsedImplicitly] 10 | public class UserClaimsProvider : IUserClaimsProvider 11 | { 12 | public Task GetClaimsAsync(string login, string password) 13 | { 14 | if (login == "lhp@lhp.com") 15 | { 16 | if (password != "1234") 17 | throw new IncorrectPasswordException(login); 18 | return Task.FromResult(new[] {new Claim(ClaimTypes.Email, login)}); 19 | } 20 | 21 | throw new LoginNotFoundException(login); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Web.Application/Web.Application.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | dmitriy.litichevskiy 7 | netcoreapp3.1 8 | portable 9 | true 10 | Web.Application 11 | Web.Application 12 | false 13 | false 14 | false 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/Web.Client/ApiClient.Account.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client 2 | { 3 | using ServicesClients.AccountController; 4 | 5 | public partial class ApiClient 6 | { 7 | private readonly AccountControllerClient _accountControllerClient; 8 | 9 | public ApiClient Login(string login, string password) 10 | { 11 | var response = _accountControllerClient.Token(login, password); 12 | CurrentState = response; 13 | _accountControllerClient.SetToken(response.Token); 14 | return this; 15 | } 16 | 17 | public ApiClient UserInfo() 18 | { 19 | CurrentState = _accountControllerClient.UserInfo(); 20 | return this; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Web.Client/ApiClient.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using Microsoft.Extensions.Options; 6 | using ServicesClients.AccountController; 7 | using ServicesClients.ValuesService; 8 | using Skeleton.Web.Integration.BaseApiFluentClient; 9 | 10 | public partial class ApiClient : BaseFluentClient 11 | { 12 | public ApiClient(HttpClient httpClient, IOptions clientOptions) 13 | { 14 | if(httpClient == null) 15 | throw new ArgumentNullException(nameof(httpClient)); 16 | if (clientOptions == null) 17 | throw new ArgumentNullException(nameof(clientOptions)); 18 | 19 | var optionsValue = clientOptions.Value; 20 | 21 | _valuesServiceClient = new ValuesServiceClient( 22 | httpClient, 23 | Options.Create( 24 | new ValuesServiceClientOptions 25 | { 26 | BaseUrl = optionsValue.BaseUrl, 27 | Timeout = optionsValue.Timeout, 28 | Serializer = optionsValue.Serializer 29 | } 30 | ) 31 | ); 32 | ServicesClients.Add(_valuesServiceClient); 33 | 34 | _accountControllerClient = new AccountControllerClient( 35 | httpClient, 36 | Options.Create( 37 | new AccountControllerClientOptions 38 | { 39 | BaseUrl = optionsValue.BaseUrl, 40 | Timeout = optionsValue.Timeout, 41 | Serializer = optionsValue.Serializer 42 | } 43 | ) 44 | ); 45 | ServicesClients.Add(_accountControllerClient); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Web.Client/ApiClientOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client 2 | { 3 | using Skeleton.Web.Integration.BaseApiClient.Configuration; 4 | 5 | public class ApiClientOptions : BaseClientOptions 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/Web.Client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Web.Client")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("1eb8cdd8-c701-4b90-a766-7a9e660e2278")] 20 | -------------------------------------------------------------------------------- /src/Web.Client/ServicesClients/AccountController/AccountControllerClientOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client.ServicesClients.AccountController 2 | { 3 | using Skeleton.Web.Integration.BaseApiClient.Configuration; 4 | 5 | public class AccountControllerClientOptions : BaseClientOptions 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/Web.Client/ServicesClients/AccountController/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client.ServicesClients.AccountController 2 | { 3 | using System; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Skeleton.Web.Integration.BaseApiClient.Configuration; 7 | using Skeleton.Web.Serialization.Abstractions; 8 | 9 | public static class ServiceCollectionExtensions 10 | { 11 | public static IHttpClientBuilder AddAccountControllerClient( 12 | this IServiceCollection services, 13 | IConfiguration config, 14 | ISerializer serializer) 15 | { 16 | if (services == null) 17 | throw new ArgumentNullException(nameof(services)); 18 | if (config == null) 19 | throw new ArgumentNullException(nameof(config)); 20 | if (serializer == null) 21 | throw new ArgumentNullException(nameof(serializer)); 22 | 23 | return services 24 | .AddClient() 25 | .ConfigureClient(config, builder => builder.WithSerializer(serializer)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Web.Client/ServicesClients/ValuesService/IValuesServiceClient.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client.ServicesClients.ValuesService 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Models.Input; 6 | using Skeleton.Web.Conventions.Responses; 7 | 8 | public interface IValuesServiceClient 9 | { 10 | IEnumerable Get(); 11 | 12 | Task> GetAsync(); 13 | 14 | string Get(int id); 15 | 16 | Task GetAsync(int id); 17 | 18 | void Set(ValuesModificationRequest request); 19 | 20 | Task SetAsync(ValuesModificationRequest request); 21 | 22 | ApiResponse Post(int id, string value); 23 | 24 | Task> PostAsync(int id, string value); 25 | 26 | void Delete(int id); 27 | 28 | Task DeleteAsync(int id); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Web.Client/ServicesClients/ValuesService/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client.ServicesClients.ValuesService 2 | { 3 | using System; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Skeleton.Web.Integration.BaseApiClient.Configuration; 7 | using Skeleton.Web.Serialization.Abstractions; 8 | 9 | public static class ServiceCollectionExtensions 10 | { 11 | public static IHttpClientBuilder AddValuesServiceClient( 12 | this IServiceCollection services, 13 | IConfiguration config, 14 | ISerializer serializer) 15 | { 16 | if (services == null) 17 | throw new ArgumentNullException(nameof(services)); 18 | if (config == null) 19 | throw new ArgumentNullException(nameof(config)); 20 | if (serializer == null) 21 | throw new ArgumentNullException(nameof(serializer)); 22 | 23 | return services 24 | .AddClient() 25 | .ConfigureClient(config, builder => builder.WithSerializer(serializer)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Web.Client/ServicesClients/ValuesService/ValuesServiceClientOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Client.ServicesClients.ValuesService 2 | { 3 | using Skeleton.Web.Integration.BaseApiClient.Configuration; 4 | 5 | public class ValuesServiceClientOptions : BaseClientOptions 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/Web.Client/Web.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | dmitriy.litichevskiy 7 | netcoreapp3.1 8 | portable 9 | Web.Client 10 | Web.Client 11 | false 12 | false 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ApiClient.cs 26 | 27 | 28 | ApiClient.cs 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Web.DataAccess/Commands/DeleteValueAsyncCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Web.DataAccess.Commands 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | using Domain.CommandContexts; 6 | using JetBrains.Annotations; 7 | using Repositories; 8 | using Skeleton.CQRS.Abstractions.Commands; 9 | 10 | [UsedImplicitly] 11 | public class DeleteValueAsyncCommand : IAsyncCommand 12 | { 13 | private readonly IValuesRepository _repository; 14 | 15 | public DeleteValueAsyncCommand(IValuesRepository repository) 16 | { 17 | if (repository == null) 18 | throw new ArgumentNullException(nameof(repository)); 19 | 20 | _repository = repository; 21 | } 22 | 23 | public Task Execute(DeleteValueAsyncCommandContext commandContext) 24 | { 25 | _repository.Delete(commandContext.Key); 26 | return Task.FromResult(0); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Web.DataAccess/Commands/SetValueCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Web.DataAccess.Commands 2 | { 3 | using System; 4 | using Domain.CommandContexts; 5 | using JetBrains.Annotations; 6 | using Repositories; 7 | using Skeleton.CQRS.Abstractions.Commands; 8 | 9 | [UsedImplicitly] 10 | public class SetValueCommand : ICommand 11 | { 12 | private readonly IValuesRepository _repository; 13 | 14 | public SetValueCommand(IValuesRepository repository) 15 | { 16 | if (repository == null) 17 | throw new ArgumentNullException(nameof(repository)); 18 | 19 | _repository = repository; 20 | } 21 | 22 | public void Execute(SetValueCommandContext commandContext) 23 | { 24 | _repository.Set(commandContext.Key, commandContext.Value); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Web.DataAccess/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Web.DataAccess")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("13f2aac7-af9d-4e1a-ac2d-86b011ed4450")] 20 | -------------------------------------------------------------------------------- /src/Web.DataAccess/Queries/GetAllValuesQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Web.DataAccess.Queries 2 | { 3 | using System; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Domain.Criteria; 7 | using Domain.Dtos; 8 | using JetBrains.Annotations; 9 | using Microsoft.Extensions.Options; 10 | using Repositories; 11 | using Skeleton.CQRS.Abstractions.Queries; 12 | 13 | [UsedImplicitly] 14 | public class GetAllValuesQuery : IAsyncQuery 15 | { 16 | private readonly IValuesRepository _repository; 17 | private readonly DefaultConfigurationValues _defaultConfigurationValues; 18 | 19 | public GetAllValuesQuery(IValuesRepository repository, IOptions defaultConfigurationValues) 20 | { 21 | if (repository == null) 22 | throw new ArgumentNullException(nameof(repository)); 23 | if (defaultConfigurationValues == null) 24 | throw new ArgumentNullException(nameof(defaultConfigurationValues)); 25 | 26 | _repository = repository; 27 | _defaultConfigurationValues = defaultConfigurationValues.Value; 28 | } 29 | 30 | public Task Ask(GetAllValuesQueryCriterion criterion) 31 | { 32 | var values = _repository.Get(); 33 | var valuesArray = values as string[] ?? values.ToArray(); 34 | 35 | return Task.FromResult(valuesArray.Length == 0 ? _defaultConfigurationValues.Values : valuesArray); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Web.DataAccess/Queries/GetValueQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Web.DataAccess.Queries 2 | { 3 | using System; 4 | using Domain.Criteria; 5 | using JetBrains.Annotations; 6 | using Repositories; 7 | using Skeleton.CQRS.Abstractions.Queries; 8 | 9 | [UsedImplicitly] 10 | public class GetValueQuery : IQuery 11 | { 12 | private readonly IValuesRepository _repository; 13 | 14 | public GetValueQuery(IValuesRepository repository) 15 | { 16 | if (repository == null) 17 | throw new ArgumentNullException(nameof(repository)); 18 | 19 | _repository = repository; 20 | } 21 | 22 | public string Ask(GetValueQueryCriterion criterion) 23 | { 24 | return _repository.Get(criterion.Key); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Web.DataAccess/Repositories/IValuesRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Web.DataAccess.Repositories 2 | { 3 | using System.Collections.Generic; 4 | 5 | public interface IValuesRepository 6 | { 7 | /// 8 | /// Returns all stored values 9 | /// 10 | /// All stored values 11 | IEnumerable Get(); 12 | 13 | /// 14 | /// Return value for key from storage 15 | /// 16 | /// Storage key 17 | /// Value for key 18 | /// Value with 19 | /// does not exist in the storage. 20 | TValue Get(int key); 21 | 22 | /// 23 | /// Set value for key to storage 24 | /// 25 | /// Storage key 26 | /// Value for key 27 | void Set(int key, TValue value); 28 | 29 | /// 30 | /// Delete value by key from storage 31 | /// 32 | /// Storage key 33 | void Delete(int key); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Web.DataAccess/Repositories/Impl/InMemoryValuesRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Web.DataAccess.Repositories.Impl 2 | { 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using JetBrains.Annotations; 7 | 8 | [UsedImplicitly] 9 | public class InMemoryValuesRepository : IValuesRepository 10 | { 11 | private readonly ConcurrentDictionary _storage; 12 | 13 | public InMemoryValuesRepository() 14 | { 15 | _storage = new ConcurrentDictionary(); 16 | } 17 | 18 | public IEnumerable Get() 19 | { 20 | return _storage.Values.ToArray(); 21 | } 22 | 23 | public TValue Get(int key) 24 | { 25 | return _storage[key]; 26 | } 27 | 28 | public void Set(int key, TValue value) 29 | { 30 | _storage.AddOrUpdate(key, value, (k, v) => value); 31 | } 32 | 33 | public void Delete(int key) 34 | { 35 | TValue value; 36 | _storage.TryRemove(key, out value); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Web.DataAccess/Web.DataAccess.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | dmitriy.litichevskiy 7 | netstandard2.0 8 | portable 9 | Web.DataAccess 10 | Web.DataAccess 11 | false 12 | false 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Web.Domain/CommandContexts/DeleteValueAsyncCommandContext.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Domain.CommandContexts 2 | { 3 | using Skeleton.CQRS.Abstractions.Commands; 4 | 5 | public class DeleteValueAsyncCommandContext : ICommandContext 6 | { 7 | public int Key { get; } 8 | 9 | public DeleteValueAsyncCommandContext(int key) 10 | { 11 | Key = key; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Web.Domain/CommandContexts/SetValueCommandContext.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Domain.CommandContexts 2 | { 3 | using Skeleton.CQRS.Abstractions.Commands; 4 | public class SetValueCommandContext : ICommandContext 5 | { 6 | public int Key { get; } 7 | public string Value { get; } 8 | 9 | public SetValueCommandContext(int key, string value) 10 | { 11 | Key = key; 12 | Value = value; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Web.Domain/Criteria/GetAllValuesQueryCriterion.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Domain.Criteria 2 | { 3 | using Skeleton.CQRS.Abstractions.Queries; 4 | public class GetAllValuesQueryCriterion : IAsyncCriterion 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /src/Web.Domain/Criteria/GetValueQueryCriterion.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Domain.Criteria 2 | { 3 | using Skeleton.CQRS.Abstractions.Queries; 4 | 5 | public class GetValueQueryCriterion : ICriterion 6 | { 7 | public int Key { get; } 8 | 9 | public GetValueQueryCriterion(int key) 10 | { 11 | Key = key; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Web.Domain/Dtos/DefaultConfigurationValues.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Domain.Dtos 2 | { 3 | public class DefaultConfigurationValues 4 | { 5 | public string[] Values { get; set; } 6 | 7 | public string Value { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Web.Domain/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Web.Domain")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("26cba7c3-0483-4d51-892b-94792c44183e")] 20 | -------------------------------------------------------------------------------- /src/Web.Domain/Web.Domain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | dmitriy.litichevskiy 7 | netstandard1.6 8 | portable 9 | Web.Domain 10 | Web.Domain 11 | false 12 | false 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Web.Migrations/Migration201703191215.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Migrations 2 | { 3 | using FluentMigrator; 4 | using JetBrains.Annotations; 5 | 6 | [UsedImplicitly] 7 | [Migration(201703191215)] 8 | public class Migration201703191215 : ForwardOnlyMigration 9 | { 10 | public override void Up() 11 | { 12 | Execute.Sql("select 0"); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Web.Migrations/Web.Migrations.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | dmitriy.litichevskiy 7 | netstandard2.0 8 | portable 9 | Web.Migrations 10 | Web.Migrations 11 | false 12 | false 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Web.Models/Input/ValuesModificationRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Models.Input 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Runtime.Serialization; 5 | 6 | /// 7 | /// Request for configuration values modification 8 | /// 9 | [DataContract] 10 | public class ValuesModificationRequest 11 | { 12 | /// 13 | /// Collection of new configuration values 14 | /// 15 | [Required] 16 | [DataMember(EmitDefaultValue = false, IsRequired = true, Order = 1)] 17 | public ConfigurationValue[] Values { get; set; } 18 | } 19 | 20 | /// 21 | /// Configuration value 22 | /// 23 | [DataContract] 24 | public class ConfigurationValue 25 | { 26 | /// 27 | /// Configuration value Id 28 | /// 29 | [DataMember(EmitDefaultValue = false, IsRequired = true, Order = 1)] 30 | public int Id { get; set; } 31 | 32 | /// 33 | /// Configuration value 34 | /// 35 | [DataMember(EmitDefaultValue = false, IsRequired = true, Order = 2)] 36 | public string Value { get; set; } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Web.Models/Web.Models.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netstandard2.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Web/Installers/CommandsInstaller.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Installers 2 | { 3 | using System.Linq; 4 | using System.Reflection; 5 | using Autofac; 6 | using DataAccess.Commands; 7 | using JetBrains.Annotations; 8 | using Skeleton.CQRS.Abstractions.Commands; 9 | using Skeleton.CQRS.Implementations.Commands; 10 | using Skeleton.CQRS.Implementations.Commands.CommandsFactory; 11 | using Module = Autofac.Module; 12 | 13 | [UsedImplicitly] 14 | public class CommandsInstaller : Module 15 | { 16 | protected override void Load(ContainerBuilder builder) 17 | { 18 | builder.RegisterType().As().SingleInstance(); 19 | builder.RegisterType().As().SingleInstance(); 20 | 21 | var commandType = typeof(ICommand<>); 22 | var asyncCommandType = typeof(IAsyncCommand<>); 23 | var dataAccess = typeof(SetValueCommand).GetTypeInfo().Assembly; 24 | builder.RegisterAssemblyTypes(dataAccess) 25 | .Where(x => x.GetInterfaces() 26 | .SingleOrDefault(i => i.GetGenericArguments().Length > 0 27 | && (i.GetGenericTypeDefinition() == commandType || i.GetGenericTypeDefinition() == asyncCommandType)) 28 | != null) 29 | .AsImplementedInterfaces() 30 | .SingleInstance(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Web/Installers/DataAccessInstaller.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Installers 2 | { 3 | using Autofac; 4 | using DataAccess.Repositories; 5 | using DataAccess.Repositories.Impl; 6 | using JetBrains.Annotations; 7 | using Skeleton.Dapper.ConnectionsFactory; 8 | using Skeleton.Dapper.SessionsFactory; 9 | 10 | [UsedImplicitly] 11 | public class DataAccessInstaller : Module 12 | { 13 | protected override void Load(ContainerBuilder builder) 14 | { 15 | builder.RegisterType().As().SingleInstance(); 16 | builder.RegisterType().As().SingleInstance(); 17 | builder.RegisterType>().As>().SingleInstance(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Web/Installers/QueriesInstaller.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Installers 2 | { 3 | using System.Linq; 4 | using System.Reflection; 5 | using Autofac; 6 | using DataAccess.Queries; 7 | using JetBrains.Annotations; 8 | using Skeleton.CQRS.Abstractions.Queries; 9 | using Skeleton.CQRS.Implementations.Queries; 10 | using Skeleton.CQRS.Implementations.Queries.QueriesFactory; 11 | using Module = Autofac.Module; 12 | 13 | [UsedImplicitly] 14 | public class QueriesInstaller : Module 15 | { 16 | protected override void Load(ContainerBuilder builder) 17 | { 18 | builder.RegisterType().As().SingleInstance(); 19 | builder.RegisterType().As().SingleInstance(); 20 | 21 | var queryType = typeof(IQuery<,>); 22 | var dataAccess = typeof(GetValueQuery).GetTypeInfo().Assembly; 23 | builder.RegisterAssemblyTypes(dataAccess) 24 | .Where(x => x.GetInterfaces() 25 | .SingleOrDefault(i => i.GetGenericArguments().Length > 0 && i.GetGenericTypeDefinition() == queryType) != null) 26 | .AsImplementedInterfaces() 27 | .SingleInstance(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Web/Installers/ServicesInstaller.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Installers 2 | { 3 | using Application.Services; 4 | using Autofac; 5 | using JetBrains.Annotations; 6 | using Skeleton.Web.Authentication.JwtBearer; 7 | using Skeleton.Web.Authentication.JwtBearer.UserClaimsProvider; 8 | 9 | [UsedImplicitly] 10 | public class ServicesInstaller : Module 11 | { 12 | protected override void Load(ContainerBuilder builder) 13 | { 14 | builder.RegisterType().As().SingleInstance(); 15 | builder.RegisterType().As().SingleInstance(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Web/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Web 2 | { 3 | using JetBrains.Annotations; 4 | using Microsoft.Extensions.Hosting; 5 | using Skeleton.Web.Hosting; 6 | 7 | [UsedImplicitly(ImplicitUseTargetFlags.Members)] 8 | public static class Program 9 | { 10 | public static IHostBuilder CreateHostBuilder(string[] args) => 11 | HostBuilderExtensions.CreateDefaultWebApiHostBuilder(args); 12 | 13 | public static void Main(string[] args) => 14 | CreateHostBuilder(args).Build().Run(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Web/Properties/PublishProfiles/DevelopmentProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | MSDeploy 9 | Release 10 | Any CPU 11 | http://localhost:8081 12 | True 13 | False 14 | netcoreapp1.1 15 | 1ca80f6c-0261-4a43-bf96-d15e372fbda3 16 | localhost 17 | WebInfrastructureDemo 18 | 19 | True 20 | InProc 21 | False 22 | 23 | <_SavePWD>False 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Web/Properties/PublishProfiles/StagingProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | FileSystem 9 | FileSystem 10 | Release 11 | Any CPU 12 | 13 | True 14 | False 15 | 16 | 1ca80f6c-0261-4a43-bf96-d15e372fbda3 17 | bin\Release\StagingPublish 18 | False 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:54946/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "WebDevelopment": { 19 | "commandName": "Project", 20 | "commandLineArgs": "--environment Development --api_route_preffix api", 21 | "applicationUrl": "http://localhost:5000/", 22 | "launchBrowser": true, 23 | "launchUrl": "http://localhost:5000/swagger/" 24 | }, 25 | "WebStaging": { 26 | "commandName": "Project", 27 | "commandLineArgs": "--environment Staging --api_route_preffix api", 28 | "applicationUrl": "http://localhost:5000/", 29 | "launchBrowser": true, 30 | "launchUrl": "http://localhost:5000/api/values" 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Web/Web.Generic.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Web/Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | dmitriy.litichevskiy 7 | netcoreapp3.1 8 | portable 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Always 32 | NLog.Staging.config 33 | 34 | 35 | Always 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = localhost,1433 Catalog=tempdb;User Id=sa;Password=k179%HC_D5A" 4 | }, 5 | 6 | "DefaultConfigurationValues": { 7 | "Value": "value_development", 8 | "Values": [ 9 | "value1_development", 10 | "value2_development" 11 | ] 12 | }, 13 | 14 | "TokensSigningKey": "GAU1022d6R131C3Dq81u47g7w0jSN2299MPd2Q8r0U13BJk7aF" 15 | } -------------------------------------------------------------------------------- /src/Web/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = (local)\\SQL2016;Initial Catalog=tempdb;User Id=sa;Password=Password12!" 4 | }, 5 | 6 | "DefaultConfigurationValues": { 7 | "Value": "value_staging", 8 | "Values": [ 9 | "value1_staging", 10 | "value2_staging" 11 | ] 12 | }, 13 | 14 | "TokensSigningKey": "DuNzOJJSWhqdUNIyfQaQUSf9cEnSynSDrekYLeywqPTXlVyCqY" 15 | } 16 | -------------------------------------------------------------------------------- /src/Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Debug", 5 | "Override": { 6 | "Microsoft": "Warning", 7 | "System": "Warning" 8 | } 9 | }, 10 | "WriteTo": [ 11 | { 12 | "Name": "Console", 13 | "Args": { "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [v{Version}, {Level:u3} {LogEventHash}] {Message}{NewLine}{Exception}{Properties}{NewLine}" } 14 | }, 15 | { 16 | "Name": "File", 17 | "Args": { 18 | "path": "logs/common.txt", 19 | "rollingInterval": "Day", 20 | "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [v{Version}, {Level:u3} {LogEventHash}] {Message}{NewLine}{Exception}{Properties}{NewLine}" 21 | } 22 | }, 23 | { 24 | "Name": "File", 25 | "Args": { 26 | "path": "logs/error.txt", 27 | "rollingInterval": "Day", 28 | "restrictedToMinimumLevel": "Error", 29 | "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [v{Version}, {Level:u3} {LogEventHash}] {Message}{NewLine}{Exception}{Properties}{NewLine}" 30 | } 31 | }, 32 | { 33 | "Name": "File", 34 | "Args": { 35 | "path": "logs/elastic.txt", 36 | "rollingInterval": "Day", 37 | "formatter": "Skeleton.Web.Logging.Serilog.Formatting.RenderedJsonFormatter, Skeleton.Web.Logging.Serilog" 38 | } 39 | }, 40 | { 41 | "Name": "Seq", 42 | "Args": { 43 | "serverUrl": "http://localhost:5341", 44 | "compact": true 45 | } 46 | } 47 | ] 48 | } 49 | } -------------------------------------------------------------------------------- /test/Infrastructure/CQRS.Implementations.Tests/CQRS.Implementations.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netcoreapp3.1 7 | portable 8 | false 9 | Skeleton.CQRS.Implementations.Tests 10 | Skeleton.CQRS.Implementations.Tests 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/Infrastructure/Common.Tests/Common.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netcoreapp3.1 7 | portable 8 | false 9 | Skeleton.Common.Tests 10 | Skeleton.Common.Tests 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/Infrastructure/Common.Tests/Extensions/EnumExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Common.Tests.Extensions 2 | { 3 | using System.ComponentModel; 4 | using Common.Extensions; 5 | using Xunit; 6 | 7 | public class EnumExtensionsTests 8 | { 9 | private enum Marks 10 | { 11 | [Description("Bad result")] 12 | One 13 | } 14 | 15 | [Fact] 16 | public void ShouldGetDescriptionForEnumMember() 17 | { 18 | //Given 19 | const string expectedDescription = "Bad result"; 20 | 21 | // When 22 | var actualDescription = Marks.One.GetDescription(); 23 | 24 | // Then 25 | Assert.Equal(expectedDescription, actualDescription); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /test/Infrastructure/Common.Tests/Extensions/ParallelExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Common.Tests.Extensions 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Common.Extensions; 7 | using JetBrains.Annotations; 8 | using Xunit; 9 | 10 | public class ParallelExtensionsTests 11 | { 12 | public class ValidationTestCase 13 | { 14 | public IEnumerable Source { get; set; } 15 | 16 | public Func Body { get; set; } 17 | } 18 | 19 | [UsedImplicitly] 20 | public static TheoryData ForEachAsyncParametersValidationTestCases; 21 | 22 | static ParallelExtensionsTests() 23 | { 24 | ForEachAsyncParametersValidationTestCases 25 | = new TheoryData 26 | { 27 | new ValidationTestCase {Source = null, Body = x => x}, 28 | new ValidationTestCase {Source = new[] {1, 2, 3}, Body = null} 29 | }; 30 | } 31 | 32 | [Theory] 33 | [MemberData(nameof(ForEachAsyncParametersValidationTestCases))] 34 | public void ForEachAsyncShouldValidateParameters(ValidationTestCase testCase) 35 | { 36 | Assert.Throws(() => testCase.Source.ForEachAsync(testCase.Body, 1)); 37 | } 38 | 39 | [Fact] 40 | public async Task ShouldExecuteForEachAsynchronously() 41 | { 42 | // Given 43 | var source = new[] {1, 2, 3}; 44 | 45 | // When 46 | var actual = await Task.WhenAll(source.ForEachAsync(x => x * x, 1)); 47 | 48 | // Then 49 | var expected = new[] {1, 4, 9}; 50 | Assert.Equal(expected, actual); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/Infrastructure/Dapper.Tests/Dapper.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netcoreapp3.1 7 | portable 8 | false 9 | Skeleton.Dapper.Tests 10 | Skeleton.Dapper.Tests 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /test/Infrastructure/Dapper.Tests/DbUsingTestBase.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Dapper.Tests 2 | { 3 | using System; 4 | using System.Data.SqlClient; 5 | using ConnectionsFactory; 6 | using JetBrains.Annotations; 7 | using Microsoft.Extensions.Options; 8 | 9 | [UsedImplicitly] 10 | public abstract class DbUsingTestBase 11 | { 12 | private readonly bool _isAppVeyor; 13 | private readonly bool _isGithubActions; 14 | 15 | private string ConnectionString 16 | { 17 | get 18 | { 19 | if (_isAppVeyor) 20 | return @"Data Source = (local)\SQL2017;Initial Catalog=tempdb;User Id=sa;Password=Password12!"; 21 | if (_isGithubActions) 22 | return "Data Source = localhost;Initial Catalog=tempdb;User Id=sa;Password=Password12!"; 23 | 24 | return @"Data Source = (localdb)\MSSQLLocalDB;Initial Catalog = tempdb; Integrated Security = True"; 25 | } 26 | } 27 | 28 | protected readonly IConnectionsFactory ConnectionsFactory; 29 | protected readonly Func SqlConnectionsFactoryMethod; 30 | 31 | protected DbUsingTestBase() 32 | { 33 | _isAppVeyor = Environment.GetEnvironmentVariable("APPVEYOR")?.ToUpperInvariant() == "TRUE"; 34 | _isGithubActions = Environment.GetEnvironmentVariable("GITHUB_ACTIONS")?.ToUpperInvariant() == "TRUE"; 35 | 36 | ConnectionsFactory = new SqlConnectionsFactory(Options.Create(new SqlConnectionsFactoryOptions {SqlServer = ConnectionString})); 37 | SqlConnectionsFactoryMethod = () => (SqlConnection) ConnectionsFactory.Create(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/Handlers/CatchingMessageHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.Tests.Handlers 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Abstractions; 7 | 8 | public class CatchingMessageHandler : IMessageHandler 9 | { 10 | private readonly List _messages = new List(); 11 | 12 | public IReadOnlyCollection Messages => _messages; 13 | 14 | public Task Handle(TMessage message, CancellationToken cancellationToken) 15 | { 16 | _messages.Add(message); 17 | return Task.CompletedTask; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/Handlers/ThrowingExceptionMessageHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.Tests.Handlers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Abstractions; 8 | 9 | public class ThrowingExceptionMessageHandler : IMessageHandler 10 | { 11 | private int _counter; 12 | private readonly List _messages; 13 | 14 | public IReadOnlyCollection Messages => _messages; 15 | 16 | public ThrowingExceptionMessageHandler() 17 | { 18 | _counter = 0; 19 | _messages = new List(); 20 | } 21 | 22 | public Task Handle(string message, CancellationToken cancellationToken) 23 | { 24 | _counter++; 25 | if(_counter == 1) 26 | throw new InvalidOperationException(); 27 | 28 | _messages.Add(message); 29 | return Task.CompletedTask; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/ProcessingService/NotificationsProcessingService.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.Tests.ProcessingService 2 | { 3 | using Abstractions; 4 | using Abstractions.QueuesFactory; 5 | using JetBrains.Annotations; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.Options; 8 | 9 | [UsedImplicitly] 10 | public class NotificationsProcessingService : MessagesProcessingService 11 | { 12 | public NotificationsProcessingService( 13 | IGenericQueuesFactory queuesFactory, 14 | IMessageHandler messageHandler, 15 | ILogger logger, 16 | IOptions options) 17 | : base(queuesFactory, messageHandler, logger, options.Value) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/ProcessingService/NotificationsProcessingServiceOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Queues.RabbitMq.Tests.ProcessingService 2 | { 3 | using System; 4 | using Abstractions; 5 | using QueuesFactory.Configuration; 6 | 7 | public class NotificationsProcessingServiceOptions : MessagesProcessingServiceOptions 8 | { 9 | public TimeSpan MessagesRequeuingDelay { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/appsettings.Development.Appveyor.Linux.json: -------------------------------------------------------------------------------- 1 | { 2 | "QueuesFactoryOptions": { 3 | "Hosts": [ "localhost" ] 4 | }, 5 | "CompletionTimeout": "00:00:05" 6 | } 7 | -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/appsettings.Development.Appveyor.Windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "QueuesFactoryOptions": { 3 | "Hosts": [ "localhost" ] 4 | }, 5 | "CompletionTimeout": "00:00:05" 6 | } 7 | -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/appsettings.Development.Travis.json: -------------------------------------------------------------------------------- 1 | { 2 | "QueuesFactoryOptions": { 3 | "Hosts": [ "localhost" ] 4 | }, 5 | "CompletionTimeout": "00:00:05" 6 | } 7 | -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "QueuesFactoryOptions": { 3 | "Hosts": [ "localhost" ] 4 | }, 5 | "CompletionTimeout": "00:00:05" 6 | } -------------------------------------------------------------------------------- /test/Infrastructure/Queues.RabbitMq.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "QueuesFactoryOptions": { 3 | "NetworkRecoveryInterval": "00:00:10", 4 | "Credentials": { 5 | "UserName": "guest", 6 | "Password": "guest" 7 | } 8 | }, 9 | 10 | "NotificationsProcessingServiceOptions": { 11 | "ConnectionAttemptTimeout": "00:00:10", 12 | "MessagesRequeuingDelay": "00:00:05", 13 | "QueueCreationOptions": { 14 | "QueueName": "NotificationsQueue", 15 | "RetriesCount": 1, 16 | "RetryInitialTimeout": "00:00:05" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/Infrastructure/Web.Tests/Integration/BaseApiClient/HttpClientHandlerExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Tests.Integration.BaseApiClient 2 | { 3 | using System; 4 | using System.Net; 5 | using System.Net.Http; 6 | using Web.Integration.BaseApiClient.Configuration; 7 | using Xunit; 8 | 9 | public class HttpClientHandlerExtensionsTests 10 | { 11 | [Fact] 12 | public void WithAutomaticDecompressionShouldValidateParameters() 13 | { 14 | Assert.Throws(() => HttpClientHandlerExtensions.WithAutomaticDecompression(null)); 15 | } 16 | 17 | [Fact] 18 | public void ShouldChangeCompressionSettings() 19 | { 20 | // Given 21 | var handler = new HttpClientHandler(); 22 | 23 | // When 24 | handler.WithAutomaticDecompression(); 25 | 26 | // Then 27 | Assert.Equal(DecompressionMethods.Deflate | DecompressionMethods.GZip, handler.AutomaticDecompression); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /test/Infrastructure/Web.Tests/Integration/BaseApiClient/ServiceCollectionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Tests.Integration.BaseApiClient 2 | { 3 | using System; 4 | using System.Net.Http; 5 | using JetBrains.Annotations; 6 | using Web.Integration.BaseApiClient; 7 | using Web.Integration.BaseApiClient.Configuration; 8 | using Xunit; 9 | 10 | public class ServiceCollectionExtensionsTests 11 | { 12 | private interface IFakeClient 13 | { 14 | int Get(); 15 | } 16 | 17 | [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] 18 | private class FakeClient : BaseClient, IFakeClient 19 | { 20 | public FakeClient(HttpClient httpClient, BaseClientOptions options) : base(httpClient, options) 21 | { 22 | } 23 | 24 | public int Get() 25 | { 26 | return 5; 27 | } 28 | } 29 | 30 | [Fact] 31 | public void AddClientWithoutInterfaceShouldNotPermitNullServiceCollection() 32 | { 33 | Assert.Throws(() => ServiceCollectionExtensions.AddClient(null)); 34 | } 35 | 36 | [Fact] 37 | public void AddClientWithInterfaceShouldNotPermitNullServiceCollection() 38 | { 39 | Assert.Throws(() => ServiceCollectionExtensions.AddClient(null)); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /test/Infrastructure/Web.Tests/Serialization/Protobuf/DateTimeOffsetSurrogateTests.cs: -------------------------------------------------------------------------------- 1 | namespace Skeleton.Web.Tests.Serialization.Protobuf 2 | { 3 | using System; 4 | using System.IO; 5 | using ProtoBuf.Meta; 6 | using Web.Serialization.Protobuf.Configuration; 7 | using Web.Serialization.Protobuf.Formatters.Surrogates; 8 | using Xunit; 9 | 10 | public class DateTimeOffsetSurrogateTests 11 | { 12 | [Fact] 13 | public void ShouldSerializeDateTimeOffset() 14 | { 15 | // Given 16 | var expected = new DateTimeOffset(2017, 10, 10, 00, 00, 00, TimeSpan.FromHours(3)); 17 | 18 | // When 19 | DateTimeOffset actual; 20 | var typeModel = RuntimeTypeModel.Create().WithTypeSurrogate(); 21 | using (var stream = new MemoryStream()) 22 | { 23 | typeModel.Serialize(stream, expected); 24 | stream.Position = 0; 25 | actual = (DateTimeOffset)typeModel.Deserialize(stream, null, typeof(DateTimeOffset)); 26 | } 27 | 28 | // Then 29 | Assert.Equal(expected, actual); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /test/Infrastructure/Web.Tests/Web.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netcoreapp3.1 7 | portable 8 | false 9 | Skeleton.Web.Tests 10 | Skeleton.Web.Tests 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/Web.Tests/ApiTestsCollection.cs: -------------------------------------------------------------------------------- 1 | namespace Web.Tests 2 | { 3 | using Skeleton.Web.Testing; 4 | using Xunit; 5 | 6 | [CollectionDefinition(nameof(ApiTestsCollection))] 7 | public class ApiTestsCollection : ICollectionFixture> 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /test/Web.Tests/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = (localdb)\\MSSQLLocalDB;Initial Catalog = tempdb; Integrated Security = True" 4 | }, 5 | 6 | "DefaultConfigurationValues": { 7 | "Value": "value_development", 8 | "Values": [ 9 | "value1_development", 10 | "value2_development" 11 | ] 12 | }, 13 | 14 | "TokensSigningKey": "GAU1022d6R131C3Dq81u47g7w0jSN2299MPd2Q8r0U13BJk7aF", 15 | 16 | "ApiTimeout": "00:10:00" 17 | } -------------------------------------------------------------------------------- /test/Web.Tests/appsettings.Staging.Appveyor.Linux.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = localhost;Initial Catalog=tempdb;User Id=sa;Password=Password12!" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/Web.Tests/appsettings.Staging.Appveyor.Windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = (local)\\SQL2016;Initial Catalog=tempdb;User Id=sa;Password=Password12!" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/Web.Tests/appsettings.Staging.Travis.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = localhost;Initial Catalog=tempdb;User Id=sa;Password=Password12!" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/Web.Tests/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "SqlServer": "Data Source = localhost;Initial Catalog=tempdb;User Id=sa;Password=k179%HC_D5A" 4 | }, 5 | 6 | "DefaultConfigurationValues": { 7 | "Value": "value_staging", 8 | "Values": [ 9 | "value1_staging", 10 | "value2_staging" 11 | ] 12 | }, 13 | 14 | "TokensSigningKey": "DuNzOJJSWhqdUNIyfQaQUSf9cEnSynSDrekYLeywqPTXlVyCqY", 15 | 16 | "ApiTimeout": "00:00:02" 17 | } 18 | -------------------------------------------------------------------------------- /test/Web.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "ValuesServiceClientOptions": { 11 | "BaseUrl": "http://localhost/", 12 | "Timeout": "00:01:00" 13 | }, 14 | "AccountControllerClientOptions": { 15 | "BaseUrl": "http://localhost/", 16 | "Timeout": "00:01:00" 17 | } 18 | } 19 | --------------------------------------------------------------------------------