├── .github ├── CODEOWNERS └── dependabot.yml ├── docs ├── _config.yml ├── logo │ ├── volley-mgmt-logo.ico │ └── volley-mgmt-logo.jpg ├── index.md ├── adr │ ├── 0005-use-azure-app-services-for-hosting.md │ ├── 0002-use-sendgrid-as-mailing-system.md │ ├── 0003-use-angular-for-spa.md │ ├── index.md │ ├── 0000-use-markdown-architectural-decision-records.md │ └── 0006-use-azure-table-storage-for-persistence.md ├── tournaments │ └── lviv-it-league-2018.html └── PRIVACY-POLICY-DEV.md ├── tests ├── unit-tests │ ├── .artifactignore │ ├── .gitignore │ ├── VolleyM.Domain.Framework.UnitTests │ │ ├── test-config.json │ │ ├── AssemblyInfo.cs │ │ ├── Fixture │ │ │ ├── SampleEvent.cs │ │ │ ├── MockedHandler.cs │ │ │ ├── NotNestedHandler.cs │ │ │ ├── RootNsHandler.cs │ │ │ ├── NoAttributeHandler.cs │ │ │ ├── NoEventSupportHandler.cs │ │ │ ├── NoValidationHandler.cs │ │ │ ├── SpyEventListener.cs │ │ │ ├── HandlerWithNullDomainEventsProperty.cs │ │ │ ├── HandlerWhichDoesNotProduceEvent.cs │ │ │ ├── TwoInterfacesHandler.cs │ │ │ ├── SampleHandler.cs │ │ │ └── SeveralEventsHandler.cs │ │ ├── AuthorizationHandlerDecorator │ │ │ └── AuthorizationDecorator.feature │ │ ├── FeatureManagement │ │ │ └── FeatureToggleDecorator.feature │ │ ├── HandlerStructure │ │ │ └── ValidatorLocator.feature │ │ ├── IDomainFrameworkTestFixture.cs │ │ ├── ValidationDecorator │ │ │ └── ValidationDecorator.feature │ │ ├── Authorization │ │ │ └── CheckAccess.feature │ │ └── EventBroker │ │ │ └── EventProducerDecorator.feature │ ├── VolleyM.Infrastructure.EventBroker.UnitTests │ │ ├── test-config.json │ │ ├── AssemblyInfo.cs │ │ ├── Fixture │ │ │ ├── ContextA │ │ │ │ ├── EventA.cs │ │ │ │ ├── EventB.cs │ │ │ │ ├── EventC.cs │ │ │ │ ├── EventD.cs │ │ │ │ ├── EventE.cs │ │ │ │ ├── EventF.cs │ │ │ │ ├── EventG.cs │ │ │ │ ├── EventH.cs │ │ │ │ ├── EventJ.cs │ │ │ │ ├── EventI.cs │ │ │ │ ├── AnotherApplicationService.cs │ │ │ │ ├── SampleEventAProducingHandler.cs │ │ │ │ ├── SampleEventBProducingHandler.cs │ │ │ │ ├── SampleEventCProducingHandler.cs │ │ │ │ ├── SampleEventDProducingHandler.cs │ │ │ │ ├── SampleEventEProducingHandler.cs │ │ │ │ ├── SampleEventFProducingHandler.cs │ │ │ │ ├── SampleEventGProducingHandler.cs │ │ │ │ ├── SampleEventHProducingHandler.cs │ │ │ │ ├── AnotherEventAProducingHandler.cs │ │ │ │ ├── SampleEventIProducingHandler.cs │ │ │ │ ├── ScopeAwareApplicationService1.cs │ │ │ │ ├── ScopeAwareApplicationService2.cs │ │ │ │ ├── ScopeAwareRequestHandler.cs │ │ │ │ └── SampleApplicationService.cs │ │ │ ├── ContextB │ │ │ │ ├── EventD.cs │ │ │ │ ├── EventF.cs │ │ │ │ ├── EventG.cs │ │ │ │ ├── EventH.cs │ │ │ │ ├── AnotherApplicationService.cs │ │ │ │ └── SamplePublicApplicationService.cs │ │ │ ├── ContextC │ │ │ │ ├── EventG.cs │ │ │ │ ├── EventI.cs │ │ │ │ ├── AnotherApplicationService.cs │ │ │ │ └── SamplePublicApplicationService.cs │ │ │ ├── EventBase.cs │ │ │ ├── IEventProducingRequest.cs │ │ │ ├── EventInvocationSpy.cs │ │ │ ├── EventProducingHandlerBase.cs │ │ │ └── ScopeLitmus.cs │ │ ├── IEventBrokerTestFixture.cs │ │ ├── UnitEventBrokerTestFixture.cs │ │ └── EvenBrokerTestSetup.cs │ ├── VolleyM.Architecture.UnitTests │ │ ├── AssemblyInfo.cs │ │ ├── TypesFixture.cs │ │ ├── TestExtensions.cs │ │ └── PackageStructureConditionExtensions.cs │ ├── VolleyM.Domain.Contributors.UnitTests │ │ ├── test-config.json │ │ ├── GetAll │ │ │ └── Contributors.feature │ │ ├── IContributorsTestFixture.cs │ │ ├── UnitContributorsTestFixture.cs │ │ └── ContributorsTestSetup.cs │ ├── VolleyM.Domain.UnitTests.Framework │ │ ├── TestTarget.cs │ │ ├── TestFixture │ │ │ ├── ITenantTestFixture.cs │ │ │ ├── EntityId.cs │ │ │ ├── IAuthFixture.cs │ │ │ ├── NoOpOneTimeTestFixture.cs │ │ │ ├── NoOpTestFixture.cs │ │ │ ├── TenantTestFixtureBase.cs │ │ │ └── ITestFixture.cs │ │ ├── Transforms │ │ │ ├── ISpecFlowTransform.cs │ │ │ ├── NoOpSpecFlowTransform.cs │ │ │ ├── ISpecFlowTransformFactory.cs │ │ │ ├── SpecFlowTransformFactory.cs │ │ │ └── Common │ │ │ │ ├── TenantIdTransform.cs │ │ │ │ └── Version │ │ │ │ └── VersionEquivalencyStep.cs │ │ ├── Constants.cs │ │ ├── TestSpyEventPublisher.cs │ │ └── VolleyM.Domain.UnitTests.Framework.csproj │ ├── VolleyM.Domain.IdentityAndAccess.UnitTests │ │ ├── Users │ │ │ ├── GetUser.feature │ │ │ └── CreateUser.feature │ │ ├── Fixture │ │ │ ├── IIdentityAndAccessFixture.cs │ │ │ ├── IdentityAndAccessFixtureBase.cs │ │ │ └── AzureCloudIdentityAndAccessOneTimeFixture.cs │ │ ├── test-config.json │ │ └── Roles │ │ │ ├── SystemRoles.feature │ │ │ └── Role.feature │ └── VolleyM.Domain.Players.UnitTests │ │ ├── Fixture │ │ ├── TestPlayerDto.cs │ │ └── AzureCloudPlayersOneTimeFixture.cs │ │ ├── EventAssertionStepsHook.cs │ │ ├── Transforms │ │ └── PlayerIdTransform.cs │ │ ├── test-config.json │ │ ├── Players │ │ ├── GetAllPlayers.feature │ │ └── CreatePlayer.feature │ │ └── IPlayersTestFixture.cs └── api-tests │ ├── extensions.json │ ├── publish-karate.azcli │ ├── set-dev-environment.ps1 │ ├── common │ └── authenticate.feature │ ├── get-karate.ps1 │ ├── Contributors │ └── hardcoded-contributors.feature │ ├── Players │ └── players.feature │ └── karate-config.js ├── global.json ├── src ├── Client │ ├── VolleyM.API.Contributors │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Contributor.cs │ │ ├── ContributorsApiMappingProfile.cs │ │ ├── VolleyM.API.Contributors.csproj │ │ ├── ContributorsApiAssemblyBootstrapper.cs │ │ └── ContributorsController.cs │ ├── VolleyM.API │ │ ├── volley-mgmt-logo.ico │ │ ├── CORS │ │ │ ├── CorsOptions.cs │ │ │ └── CorsExtensions.cs │ │ ├── esquio.json │ │ ├── Constants.cs │ │ ├── serilog.Development.json │ │ ├── Authentication │ │ │ ├── Auth0Options.cs │ │ │ └── AuthenticationExtensions.cs │ │ ├── Authorization │ │ │ ├── DefaultVolleyMAuthorizationRequirement.cs │ │ │ ├── AuthorizationExtensions.cs │ │ │ └── VolleyMDefaultAuthorizationHandler.cs │ │ ├── esquio.Staging.json │ │ ├── esquio.Development.json │ │ ├── esquio.Production.json │ │ ├── VolleyMApiApplicationInfo.cs │ │ ├── serilog.json │ │ ├── appsettings.Production.json │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── appsettings.Staging.json │ │ ├── appsettings.json │ │ ├── appsettings.Development.json │ │ ├── Extensions │ │ │ └── AppPartsExtensions.cs │ │ └── FeatureManagement │ │ │ └── ApiFeatureManager.cs │ ├── VolleyM.API.Players │ │ ├── Player.cs │ │ ├── PlayersApiMappingProfile.cs │ │ ├── VolleyM.API.Players.csproj │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── PlayersApiAssemblyBootstrapper.cs │ │ └── PlayersController.cs │ └── VolleyM.API.Contracts │ │ ├── VolleyM.API.Contracts.csproj │ │ └── Properties │ │ └── launchSettings.json ├── Infrastructure │ ├── VolleyM.Infrastructure.Players.AzureStorage │ │ ├── pluginsettings.Production.json │ │ ├── pluginsettings.Staging.json │ │ ├── pluginsettings.json │ │ ├── pluginsettings.Development.json │ │ ├── TableConfiguration │ │ │ ├── PlayersContextTableStorageOptions.cs │ │ │ └── TableConfiguration.cs │ │ ├── PlayerEntity.cs │ │ ├── VolleyM.Infrastructure.Players.AzureStorage.csproj │ │ ├── PlayersAzureStorageMigrationTask.cs │ │ ├── InfrastructurePlayersAzureStorageBootstrapper.cs │ │ └── PlayersAzureStorageMappingProfile.cs │ ├── VolleyM.Infrastructure.IdentityAndAccess.AzureStorage │ │ ├── pluginsettings.Production.json │ │ ├── pluginsettings.Staging.json │ │ ├── pluginsettings.json │ │ ├── pluginsettings.Development.json │ │ ├── TableConfiguration │ │ │ ├── IdentityContextTableStorageOptions.cs │ │ │ └── TableConfiguration.cs │ │ ├── UserEntity.cs │ │ └── IdentityAndAccessAzureStorageMigrationTask.cs │ ├── VolleyM.Infrastructure.AzureStorage │ │ ├── AzureTableStorageOptions.cs │ │ └── VolleyM.Infrastructure.AzureStorage.csproj │ ├── VolleyM.Infrastructure.EventBroker │ │ ├── IEventHandlerWrapperCache.cs │ │ ├── VolleyM.Infrastructure.EventBroker.csproj │ │ ├── EventHandlerWrapperCache.cs │ │ ├── EventHandlerWrapper.cs │ │ ├── AsyncScopedEventHandlerProxy.cs │ │ └── EventBrokerAssemblyBootstrapper.cs │ ├── VolleyM.Infrastructure.Bootstrap │ │ ├── IDomainComponentDependencyRegistrar.cs │ │ ├── VolleyM.Infrastructure.Bootstrap.csproj │ │ └── IAssemblyBootstrapper.cs │ ├── VolleyM.Infrastructure.Hardcoded │ │ ├── VolleyM.Infrastructure.Hardcoded.csproj │ │ ├── GetAllContributorsQuery.cs │ │ ├── InfrastructureHardcodedAssemblyBootstrapper.cs │ │ └── HardcodedRolesStore.cs │ └── VolleyM.Infrastructure.EventBroker.MassTransit │ │ ├── VolleyM.Infrastructure.EventBroker.MassTransit.csproj │ │ ├── EventBrokerMassTransitAssemblyBootstrapper.cs │ │ └── MassTransitEventPublisher.cs └── Domain │ ├── VolleyM.Domain.Contracts │ ├── Crosscutting │ │ ├── IRandomIdGenerator.cs │ │ ├── UserId.cs │ │ ├── IApplicationInfo.cs │ │ ├── IAuthorizationHandler.cs │ │ ├── ICurrentUserProvider.cs │ │ └── TenantId.cs │ ├── EventBroker │ │ ├── IEvent.cs │ │ ├── ICanProduceEvent.cs │ │ ├── IPublicEvent.cs │ │ └── IEventHandler.cs │ ├── Version.cs │ ├── FeatureManagement │ │ └── IFeatureManager.cs │ ├── Error │ │ ├── ValidationError.cs │ │ ├── ErrorType.cs │ │ └── Error.cs │ ├── IQuery.cs │ ├── VolleyM.Domain.Contracts.csproj │ ├── IRequestHandler.cs │ └── IRequest.cs │ ├── VolleyM.Domain.Players │ ├── PlayersConstants.cs │ ├── PlayerAggregate │ │ ├── PlayerFactory.cs │ │ ├── PlayerId.cs │ │ ├── PlayerFactoryDto.cs │ │ ├── IPlayersRepository.cs │ │ └── Player.cs │ ├── VolleyM.Domain.Players.csproj │ ├── PlayerDto.cs │ ├── DomainPlayersMappingProfile.cs │ ├── Events │ │ ├── PlayerCreated.cs │ │ └── PlayerNameCorrected.cs │ ├── Handlers │ │ ├── IPlayerNameRequest.cs │ │ └── GetAll.cs │ └── DomainPlayersAssemblyBootstrapper.cs │ ├── VolleyM.Domain.Framework │ ├── ReadMe.md │ ├── Authorization │ │ ├── CurrentUserContext.cs │ │ ├── ICurrentUserManager.cs │ │ ├── IRolesStore.cs │ │ ├── ApplicationTrustOptions.cs │ │ ├── IAuthorizationService.cs │ │ └── CurrentUserProvider.cs │ ├── EventBroker │ │ ├── IEventPublisher.cs │ │ └── NullEventPublisher.cs │ ├── RandomIdGenerator.cs │ ├── DomainFrameworkMappingProfile.cs │ ├── HandlerMetadata │ │ └── HandlerInfo.cs │ ├── VolleyM.Domain.Framework.csproj │ ├── CurrentUserScope.cs │ ├── Logging │ │ ├── LoggingRequestHandlerDecorator.cs │ │ └── LoggingQueryObjectDecorator.cs │ ├── VolleyMLifestyleSelectionBehavior.cs │ ├── DecoratorBase.cs │ ├── Validation │ │ └── ValidationHandlerDecorator.cs │ └── FrameworkDomainComponentDependencyRegistrar.cs │ ├── VolleyM.Domain.IdentityAndAccess │ ├── IdentityAndAccessConstants.cs │ ├── RolesAggregate │ │ ├── RoleId.cs │ │ └── Role.cs │ ├── UserAggregate │ │ ├── IUserRepository.cs │ │ ├── UserFactoryDto.cs │ │ ├── UserFactory.cs │ │ └── User.cs │ ├── VolleyM.Domain.IdentityAndAccess.csproj │ ├── Handlers │ │ ├── GetUser.cs │ │ └── CreateUser.cs │ └── DomainIdentityAndAccessAssemblyBootstrapper.cs │ └── VolleyM.Domain.Contributors │ ├── ContributorDto.cs │ ├── VolleyM.Domain.Contributors.csproj │ ├── GetAll.cs │ └── DomainContributorsAssemblyBootstrapper.cs ├── tools └── VolleyM.Tools.MigrationTool │ ├── appconfig.json │ ├── Contracts │ └── IMigrationTask.cs │ └── VolleyM.Tools.MigrationTool.csproj ├── dev-env ├── AppSettingsSecrets-template.config ├── run-local-app.ps1 └── build-and-run-migrations.ps1 ├── .dockerignore ├── cake.config ├── migrations └── publish-azure-storage-migration.ps1 ├── karate-tests.code-workspace └── LICENSE.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @VolleyManagement/leads -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-leap-day -------------------------------------------------------------------------------- /tests/unit-tests/.artifactignore: -------------------------------------------------------------------------------- 1 | **/* 2 | !**/*test-run.log -------------------------------------------------------------------------------- /tests/unit-tests/.gitignore: -------------------------------------------------------------------------------- 1 | # Specflow code behind 2 | *.feature.cs -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.100" 4 | } 5 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contributors/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /tools/VolleyM.Tools.MigrationTool/appconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "EnvironmentName": "Development" 3 | } -------------------------------------------------------------------------------- /dev-env/AppSettingsSecrets-template.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /docs/logo/volley-mgmt-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VolleyManagement/volley-management/HEAD/docs/logo/volley-mgmt-logo.ico -------------------------------------------------------------------------------- /docs/logo/volley-mgmt-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VolleyManagement/volley-management/HEAD/docs/logo/volley-mgmt-logo.jpg -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationTrust": { 3 | "Auth0ClientId": "someIdString" 4 | } 5 | } -------------------------------------------------------------------------------- /tests/api-tests/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "kirkslota.karate-runner", 4 | "alexkrechik.cucumberautocomplete" 5 | ] 6 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/volley-mgmt-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VolleyManagement/volley-management/HEAD/src/Client/VolleyM.API/volley-mgmt-logo.ico -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationTrust": { 3 | "Auth0ClientId": "someIdString" 4 | } 5 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/pluginsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "PlayersContextTableStorageOptions": { 3 | "PlayersTable": "players" 4 | } 5 | } -------------------------------------------------------------------------------- /dev-env/run-local-app.ps1: -------------------------------------------------------------------------------- 1 | docker build -t volleymanagement/api:local . 2 | docker run -d --rm --name vm-api -p 5000:80 --env ASPNETCORE_ENVIRONMENT=Development volleymanagement/api:local -------------------------------------------------------------------------------- /src/Client/VolleyM.API/CORS/CorsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.API.CORS 2 | { 3 | public class CorsOptions 4 | { 5 | public string AllowedOrigins { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/esquio.json: -------------------------------------------------------------------------------- 1 | { 2 | "Esquio": { 3 | // leave empty and allow each env specific config to define toggles to prevent mix ups between different files. 4 | } 5 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/pluginsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "PlayersContextTableStorageOptions": { 3 | "PlayersTable": "players0staging" 4 | } 5 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Architecture.UnitTests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | // All tests in this assembly should be unit tests 4 | [assembly: AssemblyTrait("Category", "unit")] -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | // All tests in this assembly should be unit tests 4 | [assembly: AssemblyTrait("Category", "unit")] -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/pluginsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "IdentityContextTableStorageOptions": { 3 | "UsersTable": "users" 4 | } 5 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/pluginsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "IdentityContextTableStorageOptions": { 3 | "UsersTable": "users0staging" 4 | } 5 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | // All tests in this assembly should be unit tests 4 | [assembly: AssemblyTrait("Category", "unit")] -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.API 2 | { 3 | internal static class Constants 4 | { 5 | internal const string VM_PLUGIN_PATH = "VM_PLUGIN_PATH_KEY"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Crosscutting/IRandomIdGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts.Crosscutting 2 | { 3 | public interface IRandomIdGenerator 4 | { 5 | string GetRandomId(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayersConstants.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Players 2 | { 3 | public static class PlayersConstants 4 | { 5 | public static string Name { get; } = "Players"; 6 | } 7 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Contributors.UnitTests/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "TestTarget": "Unit", 3 | "TestLogName": "contributors-test-run.log", 4 | "ApplicationTrust": { 5 | "Auth0ClientId": "someIdString" 6 | } 7 | } -------------------------------------------------------------------------------- /dev-env/build-and-run-migrations.ps1: -------------------------------------------------------------------------------- 1 | # Builds migration host and related task 2 | .\migrations\publish-azure-storage-migration.ps1 3 | cd .\migrations\azure-storage-migrations 4 | dotnet VolleyM.Tools.MigrationTool.dll 5 | cd ..\.. -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Volley Management System 2 | 3 | This is GitHub pages test for VM. We hope it will grow more in future. 4 | 5 | ## Some useful links 6 | 7 | - [Lviv IT Voleyball League Spring 2018](tournaments/lviv-it-league-2018) -------------------------------------------------------------------------------- /src/Client/VolleyM.API/serilog.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "LevelSwitches": { 4 | "$appLogLevel": "Debug", 5 | "$msfLogLevel": "Information", 6 | "$sysLogLevel": "Error" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/pluginsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "PlayersContextTableStorageOptions": { 3 | "ConnectionString": "UseDevelopmentStorage=true;", 4 | "PlayersTable": "players" 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventA.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 2 | { 3 | public class EventA : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventB.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 2 | { 3 | public class EventB : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventC.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 2 | { 3 | public class EventC : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextB/EventD.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextB 2 | { 3 | public class EventD : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextB/EventF.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextB 2 | { 3 | public class EventF : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextB/EventG.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextB 2 | { 3 | public class EventG : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextB/EventH.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextB 2 | { 3 | public class EventH : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextC/EventG.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextC 2 | { 3 | public class EventG : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextC/EventI.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextC 2 | { 3 | public class EventI : EventBase 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestTarget.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.UnitTests.Framework 2 | { 3 | public enum TestTarget 4 | { 5 | Unit, 6 | AzureCloud, 7 | OnPremSql 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/pluginsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IdentityContextTableStorageOptions": { 3 | "ConnectionString": "UseDevelopmentStorage=true;", 4 | "UsersTable": "users" 5 | } 6 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/ReadMe.md: -------------------------------------------------------------------------------- 1 | # VolleyM.Domain.Framework 2 | 3 | The purpose of this package is to handle and provide crosscutting domain concerns like authorization or logging. 4 | And implement some services defined in Domain.Contracts -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/pluginsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "PlayersContextTableStorageOptions": { 3 | "ConnectionString": "UseDevelopmentStorage=true;", 4 | "PlayersTable": "players0development" 5 | } 6 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/EventBroker/IEvent.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts.EventBroker 2 | { 3 | /// 4 | /// Represents event 5 | /// 6 | public interface IEvent 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Authentication/Auth0Options.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.API.Authentication 2 | { 3 | public sealed class Auth0Options 4 | { 5 | public string Domain { get; set; } 6 | public string ApiIdentifier { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/IdentityAndAccessConstants.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.IdentityAndAccess 2 | { 3 | public static class IdentityAndAccessConstants 4 | { 5 | public static string Context => "IdentityAndAccess"; 6 | } 7 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.AzureStorage/AzureTableStorageOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.AzureStorage 2 | { 3 | public class AzureTableStorageOptions 4 | { 5 | public string ConnectionString { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/pluginsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "IdentityContextTableStorageOptions": { 3 | "ConnectionString": "UseDevelopmentStorage=true;", 4 | "UsersTable": "users0development" 5 | } 6 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/EventBroker/ICanProduceEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace VolleyM.Domain.Contracts.EventBroker 4 | { 5 | public interface ICanProduceEvent 6 | { 7 | List DomainEvents { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Authorization/CurrentUserContext.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.IdentityAndAccess; 2 | 3 | namespace VolleyM.Domain.Framework.Authorization 4 | { 5 | public class CurrentUserContext 6 | { 7 | public User User { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Authorization/DefaultVolleyMAuthorizationRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace VolleyM.API.Authorization 4 | { 5 | public class DefaultVolleyMAuthorizationRequirement : IAuthorizationRequirement 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/ITenantTestFixture.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | 3 | namespace VolleyM.Domain.UnitTests.Framework 4 | { 5 | public interface ITenantTestFixture : ITestFixture 6 | { 7 | TenantId CurrentTenant { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/SampleEvent.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 4 | { 5 | public class SampleEvent : IEvent 6 | { 7 | public string Data { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventD.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 4 | { 5 | public class EventD : EventBase, IPublicEvent 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventE.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 4 | { 5 | public class EventE : EventBase, IPublicEvent 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventF.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 4 | { 5 | public class EventF : EventBase, IPublicEvent 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventG.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 4 | { 5 | public class EventG : EventBase, IPublicEvent 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventH.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 4 | { 5 | public class EventH : EventBase, IPublicEvent 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/IEventBrokerTestFixture.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.UnitTests.Framework; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests 4 | { 5 | public interface IEventBrokerTestFixture : ITestFixture 6 | { 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Crosscutting/UserId.cs: -------------------------------------------------------------------------------- 1 | using Destructurama.Attributed; 2 | 3 | namespace VolleyM.Domain.Contracts 4 | { 5 | [LogAsScalar()] 6 | public class UserId : ImmutableBase 7 | { 8 | public UserId(string id) : base(id) { } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Players/Player.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.API.Players 2 | { 3 | public class Player 4 | { 5 | public string Tenant { get; set; } 6 | 7 | public string Id { get; set; } 8 | 9 | public string FirstName { get; set; } 10 | 11 | public string LastName { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "docker" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | 8 | - package-ecosystem: "nuget" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | open-pull-requests-limit: 20 -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/EntityId.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | 3 | namespace VolleyM.Domain.UnitTests.Framework 4 | { 5 | public class EntityId : ImmutableBase 6 | { 7 | public EntityId(string value) : base(value) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/api-tests/publish-karate.azcli: -------------------------------------------------------------------------------- 1 | az devops login 2 | 3 | az artifacts universal publish --organization "https://dev.azure.com/VolleyManagement/" --feed "VolleyManagement" --name "karate" --version "0.9.6-rc2" --description "Karate Standalone" --path C:\Users\sdiac\Downloads\karate-0.9.6.RC2\karate-0.9.6.RC2\karate.jar -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Transforms/ISpecFlowTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VolleyM.Domain.UnitTests.Framework 4 | { 5 | public interface ISpecFlowTransform 6 | { 7 | Type TargetType { get; } 8 | 9 | object GetValue(object instance, string rawValue); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contributors/Contributor.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.API.Contributors 2 | { 3 | public class Contributor 4 | { 5 | public string FullName { get; set; } 6 | 7 | public string Team { get; set; } 8 | 9 | public string CourseDirection { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/EventBroker/IPublicEvent.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts.EventBroker 2 | { 3 | /// 4 | /// arks event which should be routed outside of the bounded context 5 | /// 6 | public interface IPublicEvent : IEvent 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Authorization/ICurrentUserManager.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Framework.Authorization 2 | { 3 | public interface ICurrentUserManager 4 | { 5 | CurrentUserContext Context { get; set; } 6 | 7 | CurrentUserScope BeginScope(CurrentUserContext userScope); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker/IEventHandlerWrapperCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker 4 | { 5 | public interface IEventHandlerWrapperCache 6 | { 7 | EventHandlerWrapper GetOrAdd(Type eventType, Func factory); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contributors/ContributorDto.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contributors 2 | { 3 | public class ContributorDto 4 | { 5 | public string FullName { get; set; } 6 | 7 | public string Team { get; set; } 8 | 9 | public string CourseDirection { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Crosscutting/IApplicationInfo.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts.Crosscutting 2 | { 3 | 4 | /// 5 | /// Provides information about the overall application 6 | /// 7 | public interface IApplicationInfo 8 | { 9 | bool IsRunningInProduction { get; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayerAggregate/PlayerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Players.PlayerAggregate 2 | { 3 | public sealed class PlayerFactory 4 | { 5 | public Player Create(PlayerFactoryDto dto) 6 | { 7 | return new Player(dto.Tenant,dto.Version, dto.Id, dto.FirstName, dto.LastName); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayerAggregate/PlayerId.cs: -------------------------------------------------------------------------------- 1 | using Destructurama.Attributed; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Players.PlayerAggregate 5 | { 6 | [LogAsScalar] 7 | public class PlayerId : ImmutableBase 8 | { 9 | public PlayerId(string id) : base(id) { } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Crosscutting/IAuthorizationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using LanguageExt; 3 | 4 | namespace VolleyM.Domain.Contracts.Crosscutting 5 | { 6 | public interface IAuthorizationHandler 7 | { 8 | EitherAsync AuthorizeUser(ClaimsPrincipal user); 9 | } 10 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Contributors.UnitTests/GetAll/Contributors.feature: -------------------------------------------------------------------------------- 1 | Feature: Contributors 2 | 3 | All contributors should be recognized and returned 4 | 5 | @unit 6 | Scenario: Query all contributors 7 | Given several contributors exist 8 | When I query all contributors 9 | Then all contributors received 10 | 11 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventJ.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 2 | { 3 | public class EventJ : EventBase 4 | { 5 | public string RequestScope { get; set; } 6 | 7 | public string EventHandlerScope { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Authorization/IRolesStore.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 4 | 5 | namespace VolleyM.Domain.Framework.Authorization 6 | { 7 | public interface IRolesStore 8 | { 9 | EitherAsync Get(RoleId roleId); 10 | } 11 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/EventI.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 4 | { 5 | public class EventI : EventBase, IPublicEvent 6 | { 7 | public string IgnoredProperty { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/api-tests/set-dev-environment.ps1: -------------------------------------------------------------------------------- 1 | # It's ok to have this secret here as it cannot be leveraged against production environment 2 | $Auth0_Dev = "NKn65TQ-BhkYaIkPRG8iUruIMjwiTzN01xUc4CwM7ucNnyEeV4RXTOgLuxu97Emj" 3 | [Environment]::SetEnvironmentVariable("VM_KARATE_AUTH0_CLIENT_SECRET", $Auth0_Dev, "User") 4 | $Env:VM_KARATE_AUTH0_CLIENT_SECRET = $Auth0_Dev 5 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Architecture.UnitTests/TypesFixture.cs: -------------------------------------------------------------------------------- 1 | using NetArchTest.Rules; 2 | 3 | namespace VolleyM.Architecture.UnitTests 4 | { 5 | public class TypesFixture 6 | { 7 | internal static Types AllProjectTypes() 8 | { 9 | return Types.InAssemblies(AssembliesFixture.AllAssemblies); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/EventBroker/IEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Domain.Framework.EventBroker 5 | { 6 | public interface IEventPublisher 7 | { 8 | Task PublishEvent(TEvent @event) 9 | where TEvent: IEvent; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/RolesAggregate/RoleId.cs: -------------------------------------------------------------------------------- 1 | using Destructurama.Attributed; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.IdentityAndAccess.RolesAggregate 5 | { 6 | [LogAsScalar] 7 | public class RoleId : ImmutableBase 8 | { 9 | public RoleId(string value) : base(value) { } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/VolleyM.Domain.Players.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.vs 6 | **/.vscode 7 | **/*.*proj.user 8 | **/azds.yaml 9 | **/charts 10 | **/bin 11 | **/obj 12 | **/Dockerfile 13 | **/Dockerfile.develop 14 | **/docker-compose.yml 15 | **/docker-compose.*.yml 16 | **/*.dbmdl 17 | **/*.jfm 18 | **/secrets.dev.yaml 19 | **/values.dev.yaml 20 | **/.toolstarget -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Players/PlayersApiMappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace VolleyM.API.Players 4 | { 5 | public class PlayersApiMappingProfile : Profile 6 | { 7 | public PlayersApiMappingProfile() 8 | { 9 | CreateMap(); 10 | CreateMap(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/RandomIdGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using VolleyM.Domain.Contracts.Crosscutting; 3 | 4 | namespace VolleyM.Domain.Framework 5 | { 6 | public class RandomIdGenerator : IRandomIdGenerator 7 | { 8 | public string GetRandomId() 9 | { 10 | return Guid.NewGuid().ToString("N").Substring(0, 20).ToLower(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/EventBase.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture 4 | { 5 | public abstract class EventBase : IEvent 6 | { 7 | public string SomeData { get; set; } 8 | public int RequestData { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Version.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts 2 | { 3 | public class Version : ImmutableBase 4 | { 5 | public Version(string value) : base(value) 6 | { 7 | } 8 | 9 | public static Version Initial = new Version("initial"); 10 | // Object was deleted 11 | public static Version Deleted = new Version("deleted"); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Crosscutting/ICurrentUserProvider.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts.Crosscutting 2 | { 3 | /// 4 | /// Provides information about currently logged in user 5 | /// 6 | public interface ICurrentUserProvider 7 | { 8 | UserId UserId { get; } 9 | 10 | TenantId Tenant { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/esquio.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "Esquio": { 3 | "Products": [ 4 | { 5 | "Name": "Players", 6 | "Features": [ 7 | { 8 | "Name": "GetAll", 9 | "Enabled": false, 10 | "Toggles": [] 11 | }, 12 | { 13 | "Name": "Create", 14 | "Enabled": true, 15 | "Toggles": [] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Contributors.UnitTests/IContributorsTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using VolleyM.Domain.UnitTests.Framework; 3 | 4 | namespace VolleyM.Domain.Contributors.UnitTests 5 | { 6 | public interface IContributorsTestFixture : ITestFixture 7 | { 8 | void MockSeveralContributorsExist(List testData); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/esquio.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Esquio": { 3 | "Products": [ 4 | { 5 | "Name": "Players", 6 | "Features": [ 7 | { 8 | "Name": "GetAll", 9 | "Enabled": true, 10 | "Toggles": [] 11 | }, 12 | { 13 | "Name": "Create", 14 | "Enabled": true, 15 | "Toggles": [] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/esquio.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "Esquio": { 3 | "Products": [ 4 | { 5 | "Name": "Players", 6 | "Features": [ 7 | { 8 | "Name": "GetAll", 9 | "Enabled": false, 10 | "Toggles": [] 11 | }, 12 | { 13 | "Name": "Create", 14 | "Enabled": true, 15 | "Toggles": [] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/TableConfiguration/PlayersContextTableStorageOptions.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Infrastructure.AzureStorage; 2 | 3 | namespace VolleyM.Infrastructure.Players.AzureStorage.TableConfiguration 4 | { 5 | public class PlayersContextTableStorageOptions : AzureTableStorageOptions 6 | { 7 | public string PlayersTable { get; set; } = "players"; 8 | } 9 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contributors/ContributorsApiMappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VolleyM.Domain.Contributors; 3 | 4 | namespace VolleyM.API.Contributors 5 | { 6 | public class ContributorsApiMappingProfile : Profile 7 | { 8 | public ContributorsApiMappingProfile() 9 | { 10 | CreateMap(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Transforms/NoOpSpecFlowTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VolleyM.Domain.UnitTests.Framework 4 | { 5 | internal class NoOpSpecFlowTransform : ISpecFlowTransform 6 | { 7 | public Type TargetType { get; } = typeof(object); 8 | 9 | public object GetValue(object instance, string rawValue) 10 | { 11 | return null; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/VolleyMApiApplicationInfo.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.Crosscutting; 2 | 3 | namespace VolleyM.API 4 | { 5 | public class VolleyMApiApplicationInfo : IApplicationInfo 6 | { 7 | public VolleyMApiApplicationInfo(bool isRunningInProduction) 8 | { 9 | IsRunningInProduction = isRunningInProduction; 10 | } 11 | 12 | public bool IsRunningInProduction { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Crosscutting/TenantId.cs: -------------------------------------------------------------------------------- 1 | using Destructurama.Attributed; 2 | 3 | namespace VolleyM.Domain.Contracts 4 | { 5 | [LogAsScalar] 6 | public class TenantId : ImmutableBase 7 | { 8 | public TenantId(string id) : base(id) { } 9 | 10 | public static TenantId Default { get; } = new TenantId("V011EYMG-0D29-4E9C-BF36-0074DBFC192B"); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contracts/VolleyM.API.Contracts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Authorization/ApplicationTrustOptions.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Framework.Authorization 2 | { 3 | /// 4 | /// Specifies options for configuring special trust permissions. 5 | /// 6 | public class ApplicationTrustOptions 7 | { 8 | public static string ConfigKey = "ApplicationTrust"; 9 | 10 | public string Auth0ClientId { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /cake.config: -------------------------------------------------------------------------------- 1 | ; This is the default configuration file for Cake. 2 | ; This file was downloaded from https://github.com/cake-build/resources 3 | 4 | [Nuget] 5 | Source=https://api.nuget.org/v3/index.json 6 | UseInProcessClient=true 7 | LoadDependencies=false 8 | 9 | [Paths] 10 | Tools=./build/tools 11 | Addins=./build/tools/Addins 12 | Modules=./build/tools/Modules 13 | 14 | [Settings] 15 | SkipVerification=false 16 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/FeatureManagement/IFeatureManager.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace VolleyM.Domain.Contracts.FeatureManagement 5 | { 6 | public interface IFeatureManager 7 | { 8 | Task IsEnabledAsync( 9 | string featureName, 10 | string contextName, 11 | CancellationToken cancellationToken = default(CancellationToken)); 12 | } 13 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/IEventProducingRequest.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture 2 | { 3 | /// 4 | /// Simplifies test setup for event generating handlers. See usage for more details 5 | /// 6 | public interface IEventProducingRequest 7 | { 8 | public int EventData { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Users/GetUser.feature: -------------------------------------------------------------------------------- 1 | Feature: Get User by ID 2 | Retrieve user by ID 3 | 4 | @unit @azurecloud @ab:1026 5 | Scenario: User exist 6 | Given user exists 7 | When I get user 8 | Then user is returned 9 | 10 | @unit @azurecloud @ab:1026 11 | Scenario: User does not exist 12 | Given user does not exist 13 | When I get user 14 | Then NotFound error is returned -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/TableConfiguration/IdentityContextTableStorageOptions.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Infrastructure.AzureStorage; 2 | 3 | namespace VolleyM.Infrastructure.IdentityAndAccess.AzureStorage.TableConfiguration 4 | { 5 | public class IdentityContextTableStorageOptions : AzureTableStorageOptions 6 | { 7 | public string UsersTable { get; set; } = "users"; 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/UserAggregate/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.IdentityAndAccess 5 | { 6 | public interface IUserRepository 7 | { 8 | EitherAsync Add(User user); 9 | 10 | EitherAsync Get(TenantId tenant, UserId id); 11 | 12 | EitherAsync Delete(TenantId tenant, UserId id); 13 | } 14 | } -------------------------------------------------------------------------------- /tools/VolleyM.Tools.MigrationTool/Contracts/IMigrationTask.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace VolleyM.Tools.MigrationTool.Contracts 5 | { 6 | /// 7 | /// Task to migrate data 8 | /// 9 | public interface IMigrationTask 10 | { 11 | Task Initialize(IConfiguration config); 12 | 13 | Task MigrateUp(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/serilog.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "UseElastic": false, 4 | "LevelSwitches": { 5 | "$appLogLevel": "Debug", 6 | "$msfLogLevel": "Information", 7 | "$sysLogLevel": "Error" 8 | }, 9 | "MinimumLevel": { 10 | "ControlledBy": "$appLogLevel", 11 | "Override": { 12 | "Microsoft": "$msfLogLevel", 13 | "System": "$sysLogLevel" 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/EventBroker/NullEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Domain.Framework.EventBroker 5 | { 6 | public class NullEventPublisher : IEventPublisher 7 | { 8 | public Task PublishEvent(TEvent @event) where TEvent : IEvent 9 | { 10 | return Task.CompletedTask; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayerAggregate/PlayerFactoryDto.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | 3 | namespace VolleyM.Domain.Players.PlayerAggregate 4 | { 5 | public class PlayerFactoryDto 6 | { 7 | public TenantId Tenant { get; set; } 8 | 9 | public Version Version { get; set; } 10 | 11 | public PlayerId Id { get; set; } 12 | 13 | public string FirstName { get; set; } 14 | 15 | public string LastName { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Error/ValidationError.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation.Results; 2 | 3 | namespace VolleyM.Domain.Contracts 4 | { 5 | public class ValidationError : Error 6 | { 7 | public ValidationError(ValidationResult result) 8 | : base(ErrorType.ValidationFailed, "Validation failed") 9 | { 10 | Result = result; 11 | } 12 | 13 | public ValidationResult Result { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Error/ErrorType.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts 2 | { 3 | public enum ErrorType 4 | { 5 | Unknown = 0, 6 | Conflict = 1, 7 | NotFound = 2, 8 | InternalError = 3, 9 | NotAuthorized = 4, 10 | NotAuthenticated = 5, 11 | ValidationFailed = 6, 12 | FeatureDisabled = 7, 13 | DesignViolation = 8,//internal framework error, usually should happen during development only 14 | ConcurrencyCheckFailed = 9, 15 | } 16 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/EventInvocationSpy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture 5 | { 6 | public class EventInvocationSpy 7 | { 8 | public List Invocations { get; } = new List(); 9 | 10 | public void RegisterInvocation(IEvent evt) => Invocations.Add(evt); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/DomainFrameworkMappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Framework 5 | { 6 | public class DomainFrameworkMappingProfile : Profile 7 | { 8 | public DomainFrameworkMappingProfile() 9 | { 10 | CreateMap() 11 | .ConvertUsing(t => t.ToString()); 12 | 13 | CreateMap() 14 | .ConvertUsing(t => new TenantId(t)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayerDto.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.Players.PlayerAggregate; 3 | 4 | namespace VolleyM.Domain.Players 5 | { 6 | public class PlayerDto 7 | { 8 | public TenantId Tenant { get; set; } 9 | 10 | public Version Version { get; set; } 11 | 12 | public PlayerId Id { get; set; } 13 | 14 | public string FirstName { get; set; } 15 | 16 | public string LastName { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/DomainPlayersMappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VolleyM.Domain.Players.PlayerAggregate; 3 | 4 | namespace VolleyM.Domain.Players 5 | { 6 | public class DomainPlayersMappingProfile : Profile 7 | { 8 | public DomainPlayersMappingProfile() 9 | { 10 | CreateMap() 11 | .ConvertUsing(t => t.ToString()); 12 | 13 | CreateMap() 14 | .ConvertUsing(t => new PlayerId(t)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/EventBroker/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace VolleyM.Domain.Contracts.EventBroker 4 | { 5 | public interface IEventHandler : IEventHandler 6 | where T : IEvent 7 | { 8 | Task Handle(T @event); 9 | } 10 | 11 | /// 12 | /// Represents non generic event handler to simplify event dispatching 13 | /// 14 | public interface IEventHandler 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/IQuery.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | 3 | namespace VolleyM.Domain.Contracts 4 | { 5 | /// 6 | /// Represents Query object to be executed against persistence layer 7 | /// 8 | /// Query parameters 9 | /// Query result 10 | public interface IQuery 11 | where TResult : class 12 | { 13 | EitherAsync Execute(TParam param); 14 | } 15 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Transforms/ISpecFlowTransformFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VolleyM.Domain.UnitTests.Framework 4 | { 5 | /// 6 | /// SpecFlow does not do a good job in transforms for regular tables so this interface is a way to fix that. 7 | /// 8 | public interface ISpecFlowTransformFactory 9 | { 10 | ISpecFlowTransform GetTransform(Type targetType); 11 | 12 | void RegisterTransform(ISpecFlowTransform transform); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/appsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "Auth0": { 3 | "Domain": "volley-mgmt.eu.auth0.com", 4 | "ApiIdentifier": "https://api.volley-mgmt.org.ua" 5 | }, 6 | "CORS": { 7 | "AllowedOrigins": "https://volley-mgmt.org.ua" 8 | }, 9 | "ApplicationTrust": { 10 | "Auth0ClientId": "ikfOsEbWxONYWLnt2gVPsqQXE5WGGxVy" 11 | }, 12 | "IdentityContextTableStorageOptions": { 13 | "UsersTable": "users" 14 | }, 15 | "PlayersContextTableStorageOptions": { 16 | "PlayersTable": "players" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker/VolleyM.Infrastructure.EventBroker.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/UserAggregate/UserFactoryDto.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 3 | 4 | namespace VolleyM.Domain.IdentityAndAccess 5 | { 6 | /// 7 | /// Data object to reconstruct User state from persistence 8 | /// 9 | public class UserFactoryDto 10 | { 11 | public UserId Id { get; set; } 12 | public TenantId Tenant { get; set; } 13 | public RoleId Role { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Bootstrap/IDomainComponentDependencyRegistrar.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using SimpleInjector; 4 | 5 | namespace VolleyM.Infrastructure.Bootstrap 6 | { 7 | /// 8 | /// Provides logic for common registration of Domain Components 9 | /// 10 | public interface IDomainComponentDependencyRegistrar 11 | { 12 | void RegisterCommonDependencies(Container container, List domainComponentsAssemblies); 13 | } 14 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/MockedHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Framework.UnitTests.Fixture 5 | { 6 | public class MockedHandler : IRequestHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public EitherAsync Handle(Request request) 16 | { 17 | return Unit.Default; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/Events/PlayerCreated.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | using VolleyM.Domain.Players.PlayerAggregate; 4 | 5 | namespace VolleyM.Domain.Players.Events 6 | { 7 | public class PlayerCreated : IPublicEvent 8 | { 9 | public TenantId TenantId { get; set; } 10 | 11 | public Version Version { get; set; } 12 | 13 | public PlayerId PlayerId { get; set; } 14 | 15 | public string FirstName { get; set; } 16 | 17 | public string LastName { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/NotNestedHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Framework.UnitTests.Fixture 5 | { 6 | public class NotNestedHandler : IRequestHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public EitherAsync Handle(Request request) 16 | { 17 | return Unit.Default; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Authorization/IAuthorizationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 3 | 4 | namespace VolleyM.Domain.Framework.Authorization 5 | { 6 | public interface IAuthorizationService 7 | { 8 | /// 9 | /// Validates if current user has required permission 10 | /// 11 | /// 12 | /// 13 | Task CheckAccess(Permission permission); 14 | } 15 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/RootNsHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace RootNs 5 | { 6 | public class RootNsHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public class Handler : IRequestHandler 16 | { 17 | public EitherAsync Handle(Request request) 18 | { 19 | return Unit.Default; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/HandlerMetadata/HandlerInfo.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Framework.HandlerMetadata 2 | { 3 | public class HandlerInfo 4 | { 5 | public string Context { get; } 6 | 7 | public string Action { get; } 8 | 9 | public HandlerInfo(string context, string action) 10 | { 11 | Context = context; 12 | Action = action; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return $"{Context}:{Action}"; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/Events/PlayerNameCorrected.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | using VolleyM.Domain.Players.PlayerAggregate; 4 | 5 | namespace VolleyM.Domain.Players.Events 6 | { 7 | public class PlayerNameCorrected : IPublicEvent 8 | { 9 | public TenantId TenantId { get; set; } 10 | 11 | public PlayerId PlayerId { get; set; } 12 | 13 | public Version Version { get; set; } 14 | 15 | public string FirstName { get; set; } 16 | 17 | public string LastName { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/Fixture/TestPlayerDto.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.Players.PlayerAggregate; 3 | 4 | namespace VolleyM.Domain.Players.UnitTests.Fixture 5 | { 6 | /// 7 | /// Represents player data to simplify creation of objects 8 | /// 9 | public class TestPlayerDto 10 | { 11 | public PlayerId Id { get; set; } 12 | 13 | public Version Version { get; set; } 14 | 15 | public string FirstName { get; set; } 16 | 17 | public string LastName { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/EventAssertionStepsHook.cs: -------------------------------------------------------------------------------- 1 | using SimpleInjector; 2 | using VolleyM.Domain.UnitTests.Framework; 3 | 4 | namespace VolleyM.Domain.Players.UnitTests 5 | { 6 | /// 7 | /// SpecFlow is not great at reusing steps across assemblies. 8 | /// By having this class we make it think that it belongs to this assembly 9 | /// 10 | public class EventAssertionStepsHook : EventAssertionsSteps 11 | { 12 | public EventAssertionStepsHook(Container container) 13 | : base(container) 14 | { 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VolleyM.API": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "applicationUrl": "http://localhost:5000;https://localhost:5001" 10 | }, 11 | "Docker": { 12 | "commandName": "Docker", 13 | "launchUrl": "localhost:5001", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Fixture/IIdentityAndAccessFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts; 3 | using VolleyM.Domain.UnitTests.Framework; 4 | 5 | namespace VolleyM.Domain.IdentityAndAccess.UnitTests.Fixture 6 | { 7 | public interface IIdentityAndAccessFixture : ITestFixture 8 | { 9 | Task ConfigureUserExists(TenantId tenant, UserId id, User user); 10 | 11 | Task ConfigureUserDoesNotExist(TenantId tenant, UserId id); 12 | 13 | Task VerifyUserCreated(User user); 14 | } 15 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/IAuthFixture.cs: -------------------------------------------------------------------------------- 1 | using SimpleInjector; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.UnitTests.Framework 5 | { 6 | /// 7 | /// Common fixture to handle authentication and authorization during test 8 | /// 9 | public interface IAuthFixture 10 | { 11 | void SetTestUserPermission(string context, string action); 12 | void ConfigureTestUserRole(Container container); 13 | void ConfigureTestUser(Container container, TenantId tenant); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/UserAggregate/UserFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VolleyM.Domain.IdentityAndAccess 4 | { 5 | /// 6 | /// Creates user instances from persistent state 7 | /// 8 | public sealed class UserFactory 9 | { 10 | public User CreateUser(UserFactoryDto userDto) 11 | { 12 | if (userDto == null) 13 | { 14 | throw new ArgumentNullException(nameof(userDto)); 15 | } 16 | return new User(userDto); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/NoAttributeHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Framework.UnitTests.Fixture 5 | { 6 | public class NoAttributeHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public class Handler : IRequestHandler 16 | { 17 | public EitherAsync Handle(Request request) 18 | { 19 | return Unit.Default; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/NoEventSupportHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 5 | { 6 | public class NoEventSupportHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public class Handler : IRequestHandler 16 | { 17 | public EitherAsync Handle(Request request) 18 | { 19 | return Unit.Default; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/NoValidationHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 5 | { 6 | public class NoValidationHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public class Handler : IRequestHandler 16 | { 17 | public EitherAsync Handle(Request request) 18 | { 19 | return Unit.Default; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/SpyEventListener.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using VolleyM.Domain.Contracts.EventBroker; 4 | 5 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 6 | { 7 | public class SpyEventListener : IEventHandler 8 | { 9 | public Task Handle(SampleEvent @event) 10 | { 11 | Events.Add(@event); 12 | return Task.CompletedTask; 13 | } 14 | 15 | public List Events { get; set; } = new List(); 16 | } 17 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.UnitTests.Framework 2 | { 3 | public class Constants 4 | { 5 | public const int BEFORE_SCENARIO_INIT_CONTAINER_ORDER = 100; 6 | public const int BEFORE_SCENARIO_REGISTER_DEPENDENCIES_ORDER = 200; 7 | public const int BEFORE_SCENARIO_STEPS_BASE_ORDER = 300; 8 | public const int BEFORE_SCENARIO_STEPS_ORDER = 400; 9 | 10 | public const int AFTER_SCENARIO_TEST_FRAMEWORK_ORDER = 200; 11 | public const int AFTER_SCENARIO_STEPS_ORDER = 100; 12 | } 13 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "Auth0": { 3 | "Domain": "volley-mgmt-staging.eu.auth0.com", 4 | "ApiIdentifier": "https://api.staging.volley-mgmt.org.ua" 5 | }, 6 | "CORS": { 7 | "AllowedOrigins": "https://staging-volley-mgmt.azurewebsites.net;https://staging.volley-mgmt.org.ua" 8 | }, 9 | "ApplicationTrust": { 10 | "Auth0ClientId": "gHFwhskwh3HLP7fYobiUFSW3BK8055DL" 11 | }, 12 | "IdentityContextTableStorageOptions": { 13 | "UsersTable": "users0staging" 14 | }, 15 | "PlayersContextTableStorageOptions": { 16 | "PlayersTable": "players0staging" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.AzureStorage/VolleyM.Infrastructure.AzureStorage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/NoOpOneTimeTestFixture.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace VolleyM.Domain.UnitTests.Framework 4 | { 5 | public class NoOpOneTimeTestFixture : IOneTimeTestFixture 6 | { 7 | public void OneTimeSetup(IConfiguration configuration) 8 | { 9 | // no op 10 | } 11 | 12 | public void OneTimeTearDown() 13 | { 14 | // no op 15 | } 16 | 17 | public static IOneTimeTestFixture Instance { get; } = new NoOpOneTimeTestFixture(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/Handlers/IPlayerNameRequest.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | 3 | namespace VolleyM.Domain.Players.Handlers 4 | { 5 | public interface IPlayerNameRequest 6 | { 7 | string FirstName { get; set; } 8 | 9 | string LastName { get; set; } 10 | } 11 | 12 | internal class PlayerNameValidator : AbstractValidator 13 | { 14 | public PlayerNameValidator() 15 | { 16 | RuleFor(r => r.FirstName) 17 | .NotEmpty() 18 | .MaximumLength(60); 19 | 20 | RuleFor(r => r.LastName) 21 | .NotEmpty() 22 | .MaximumLength(60); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/Transforms/PlayerIdTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using VolleyM.Domain.Players.PlayerAggregate; 3 | using VolleyM.Domain.UnitTests.Framework; 4 | 5 | namespace VolleyM.Domain.Players.UnitTests.Transforms 6 | { 7 | public class PlayerIdTransform : ISpecFlowTransform 8 | { 9 | public Type TargetType { get; } = typeof(PlayerId); 10 | 11 | public object GetValue(object instance, string rawValue) 12 | { 13 | if (string.IsNullOrEmpty(rawValue)) 14 | { 15 | return null; 16 | } 17 | 18 | return new PlayerId(rawValue); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestSpyEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using VolleyM.Domain.Contracts.EventBroker; 4 | using VolleyM.Domain.Framework.EventBroker; 5 | 6 | namespace VolleyM.Domain.UnitTests.Framework 7 | { 8 | public class TestSpyEventPublisher : IEventPublisher 9 | { 10 | public List ReceivedEvents { get; } = new List(); 11 | 12 | public Task PublishEvent(TEvent @event) where TEvent : IEvent 13 | { 14 | ReceivedEvents.Add(@event); 15 | return Task.CompletedTask; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Hardcoded/VolleyM.Infrastructure.Hardcoded.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Client/VolleyM.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "Auth0": { 4 | "Domain": "volley-mgmt-dev.eu.auth0.com", 5 | "ApiIdentifier": "https://api.dev.volley-mgmt.org.ua" 6 | }, 7 | "CORS": { 8 | "AllowedOrigins": "http://localhost:3000" 9 | }, 10 | "ApplicationTrust": { 11 | "Auth0ClientId": "someIdString" 12 | }, 13 | "IdentityContextTableStorageOptions": { 14 | "ConnectionString": "UseDevelopmentStorage=true;", 15 | "UsersTable": "users" 16 | }, 17 | "PlayersContextTableStorageOptions": { 18 | "ConnectionString": "UseDevelopmentStorage=true;", 19 | "PlayersTable": "players" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/VolleyM.Domain.IdentityAndAccess.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker/EventHandlerWrapperCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker 5 | { 6 | internal class EventHandlerWrapperCache : IEventHandlerWrapperCache 7 | { 8 | private readonly ConcurrentDictionary _cache 9 | = new ConcurrentDictionary(); 10 | 11 | public EventHandlerWrapper GetOrAdd(Type eventType, Func factory) 12 | { 13 | return _cache.GetOrAdd(eventType, t => factory()); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Fixture/IdentityAndAccessFixtureBase.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.UnitTests.Framework; 3 | 4 | namespace VolleyM.Domain.IdentityAndAccess.UnitTests.Fixture 5 | { 6 | public class IdentityAndAccessFixtureBase 7 | { 8 | public EntityId GetEntityId(object instance) 9 | { 10 | return instance switch 11 | { 12 | User u => GetIdForUser(u.Tenant, u.Id), 13 | _ => null 14 | }; 15 | } 16 | 17 | private static EntityId GetIdForUser(TenantId tenantId, UserId userId) 18 | { 19 | return new EntityId($"{tenantId}|{userId}"); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "TestTarget": "Unit", 3 | "TestLogName": "pl-test-run.log", 4 | "ApplicationTrust": { 5 | "Auth0ClientId": "someIdString" 6 | }, 7 | "PlayersContextTableStorageOptions": { 8 | "ConnectionString": "AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;DefaultEndpointsProtocol=http;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;", 9 | "PlayersTable": "players0azurecloudtests" 10 | } 11 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "TestTarget": "Unit", 3 | "TestLogName": "ia-test-run.log", 4 | "ApplicationTrust": { 5 | "Auth0ClientId": "someIdString" 6 | }, 7 | "IdentityContextTableStorageOptions": { 8 | "ConnectionString": "AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;DefaultEndpointsProtocol=http;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;", 9 | "UsersTable": "users0azurecloudtests" 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/VolleyM.Domain.Contracts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/AuthorizationHandlerDecorator/AuthorizationDecorator.feature: -------------------------------------------------------------------------------- 1 | Feature: Authorization Decorator 2 | In order to authorize access to Request Handlers 3 | we want a decorator which will authorize user against handler 4 | 5 | @ab:1026 6 | Scenario: User has permission 7 | Given current user has permission to execute this handler 8 | When I call decorated handler 9 | Then success result is returned 10 | 11 | @ab:1026 12 | Scenario: No permission 13 | Given current user has no permission to execute this handler 14 | When I call decorated handler 15 | Then NotAuthorized error should be returned with message No permission -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contributors/VolleyM.Domain.Contributors.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Hardcoded/GetAllContributorsQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contributors; 5 | 6 | namespace VolleyM.Infrastructure.Hardcoded 7 | { 8 | public class GetAllContributorsQuery : IQuery> 9 | { 10 | public EitherAsync> Execute(Unit param) => new List { 11 | new ContributorDto {FullName = "Dmytro Shapoval", CourseDirection = "All", Team = "Special"}, 12 | new ContributorDto {FullName = "Mykola Bocharskiy", CourseDirection = "All", Team = "Special"}, 13 | }; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Authorization/CurrentUserProvider.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.Contracts.Crosscutting; 3 | 4 | namespace VolleyM.Domain.Framework.Authorization 5 | { 6 | public class CurrentUserProvider : ICurrentUserProvider, ICurrentUserManager 7 | { 8 | public UserId UserId => Context?.User?.Id; 9 | 10 | public TenantId Tenant => Context?.User?.Tenant; 11 | 12 | public CurrentUserContext Context { get; set; } 13 | 14 | public CurrentUserScope BeginScope(CurrentUserContext userScope) 15 | { 16 | return new CurrentUserScope(this, userScope); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Auth0": { 3 | "Domain": "volley-mgmt-dev.eu.auth0.com", 4 | "ApiIdentifier": "https://api.dev.volley-mgmt.org.ua" 5 | }, 6 | "CORS": { 7 | "AllowedOrigins": "http://localhost:3000;http://dev.volley-mgmt.org.ua" 8 | }, 9 | "ApplicationTrust": { 10 | "Auth0ClientId": "A4tbWQojYmGgmxiFcA21KeoFbnG4c5Ex" 11 | }, 12 | "IdentityContextTableStorageOptions": { 13 | "ConnectionString": "UseDevelopmentStorage=true;", 14 | "UsersTable": "users0development" 15 | }, 16 | "PlayersContextTableStorageOptions": { 17 | "ConnectionString": "UseDevelopmentStorage=true;", 18 | "PlayersTable": "players0development" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker.MassTransit/VolleyM.Infrastructure.EventBroker.MassTransit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Roles/SystemRoles.feature: -------------------------------------------------------------------------------- 1 | Feature: System Roles 2 | Documents all permission assignments for roles 3 | 4 | @azurecloud 5 | Scenario: Visitor 6 | Given I have Visitor role 7 | When I request role from the store 8 | Then role should be found 9 | And I have following permissions 10 | | Context | Action | 11 | | contributors | getall | 12 | 13 | @azurecloud 14 | Scenario: SysAdmin 15 | Given I have SysAdmin role 16 | When I request role from the store 17 | Then role should be found 18 | And I have following permissions 19 | | Context | Action | 20 | | contributors | getall | 21 | | players | create | 22 | | players | getall | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/VolleyM.Domain.Framework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contributors/GetAll.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | 5 | namespace VolleyM.Domain.Contributors 6 | { 7 | using IQueryObject = IQuery>; 8 | 9 | public class GetAll 10 | { 11 | public class Request : IRequest> 12 | { 13 | // no params 14 | } 15 | 16 | public class Handler : IRequestHandler> 17 | { 18 | private readonly IQueryObject _query; 19 | 20 | public Handler(IQueryObject query) => _query = query; 21 | 22 | public EitherAsync> Handle(Request request) => _query.Execute(Unit.Default); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/IRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | 3 | namespace VolleyM.Domain.Contracts 4 | { 5 | /// 6 | /// Defines a handler for a request 7 | /// 8 | /// The type of request being handled 9 | /// The type of response from the handler 10 | public interface IRequestHandler 11 | where TRequest : IRequest 12 | { 13 | /// 14 | /// Handles a request 15 | /// 16 | /// The request 17 | /// Response from the request 18 | EitherAsync Handle(TRequest request); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Players/VolleyM.API.Players.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/IRequest.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | 3 | namespace VolleyM.Domain.Contracts 4 | { 5 | /// 6 | /// Marker interface to represent a request with a void response 7 | /// 8 | public interface IRequest : IRequest { } 9 | 10 | /// 11 | /// Marker interface to represent a request with a response 12 | /// 13 | /// Response type 14 | public interface IRequest : IBaseRequest { } 15 | 16 | /// 17 | /// Allows for generic type constraints of objects implementing IRequest or IRequest{TResponse} 18 | /// 19 | public interface IBaseRequest { } 20 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/TableConfiguration/TableConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using VolleyM.Infrastructure.AzureStorage; 3 | 4 | namespace VolleyM.Infrastructure.Players.AzureStorage.TableConfiguration 5 | { 6 | public class TableConfiguration : AzureTableConfiguration 7 | { 8 | private readonly PlayersContextTableStorageOptions _options; 9 | 10 | public TableConfiguration(PlayersContextTableStorageOptions options) 11 | : base(options) 12 | { 13 | _options = options; 14 | } 15 | 16 | protected override IEnumerable GetTablesForContext() 17 | { 18 | return new List 19 | { 20 | _options.PlayersTable 21 | }; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /migrations/publish-azure-storage-migration.ps1: -------------------------------------------------------------------------------- 1 | $taskProjects = ` 2 | @{folder = 'src/Infrastructure'; name = 'VolleyM.Infrastructure.IdentityAndAccess.AzureStorage' }, 3 | @{folder = 'src/Infrastructure'; name = 'VolleyM.Infrastructure.Players.AzureStorage' } 4 | 5 | $outputFolder = "./migrations/azure-storage-migrations" 6 | 7 | # deploy migration host 8 | dotnet publish './tools/VolleyM.Tools.MigrationTool/VolleyM.Tools.MigrationTool.csproj' -c Release -o $outputFolder 9 | 10 | foreach ($project in $taskProjects) { 11 | $projectPath = './{0}/{1}/{1}.csproj' -f ($project.folder, $project.name) 12 | $outFolder = '{0}/tasks/{1}' -f ($outputFolder, $project.name) 13 | 14 | dotnet publish $projectPath -c Release -o $outFolder 15 | } -------------------------------------------------------------------------------- /tests/api-tests/common/authenticate.feature: -------------------------------------------------------------------------------- 1 | @ignore 2 | Feature: Retrieve and store authentication token to test protected API parts 3 | 4 | Background: Set variables 5 | * def auth0Url = 'https://' + auth0.domain + '.eu.auth0.com' 6 | Scenario: 7 | Given url auth0Url 8 | And path 'oauth/token' 9 | And request 10 | """ 11 | { 12 | "client_id": #(auth0.clientId), 13 | "client_secret": #(auth0.clientSecret), 14 | "audience": #(auth0.audience), 15 | "grant_type": "client_credentials" 16 | } 17 | """ 18 | When method post 19 | Then status 200 20 | 21 | * def access_token = response.access_token -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contributors/VolleyM.API.Contributors.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/HandlerWithNullDomainEventsProperty.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | 6 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 7 | { 8 | public class HandlerWithNullDomainEventsProperty 9 | { 10 | public class Request : IRequest 11 | { 12 | public int A { get; set; } 13 | 14 | public int B { get; set; } 15 | } 16 | 17 | public class Handler : IRequestHandler, ICanProduceEvent 18 | { 19 | public EitherAsync Handle(Request request) 20 | { 21 | return Unit.Default; 22 | } 23 | 24 | public List DomainEvents { get; } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/FeatureManagement/FeatureToggleDecorator.feature: -------------------------------------------------------------------------------- 1 | Feature: Feature Toggle Decorator 2 | Decorator that validates state of the feature toggle for particular handler 3 | If Toggle is turned off handler is not executed and FeatureDisabled error is returned 4 | 5 | @unit @ab:996 6 | Scenario: Feature Toggle turned on 7 | Given I have a handler 8 | And feature toggle exists 9 | And feature toggle is enabled 10 | When I call decorated handler 11 | Then handler result should be returned 12 | 13 | @unit @ab:996 14 | Scenario: Feature Toggle turned off 15 | Given I have a handler 16 | And feature toggle exists 17 | And feature toggle is disabled 18 | When I call decorated handler 19 | Then feature disabled error should be returned -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/HandlerWhichDoesNotProduceEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | 6 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 7 | { 8 | public class HandlerWhichDoesNotProduceEvent 9 | { 10 | public class Request : IRequest 11 | { 12 | public int A { get; set; } 13 | 14 | public int B { get; set; } 15 | 16 | } 17 | 18 | public class Handler : IRequestHandler, ICanProduceEvent 19 | { 20 | public EitherAsync Handle(Request request) 21 | { 22 | return Unit.Default; 23 | } 24 | 25 | public List DomainEvents { get; } = new List(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contracts/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:1208/", 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 | "VolleyM.API.Contracts": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:1209/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Bootstrap/VolleyM.Infrastructure.Bootstrap.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/api-tests/get-karate.ps1: -------------------------------------------------------------------------------- 1 | $karateVersion = 'karate-0.9.9.RC3' 2 | 3 | $url = "https://dl.bintray.com/ptrthomas/karate/${karateVersion}.jar"; 4 | 5 | $karateHome = '~\.karate' 6 | If (!(test-path $karateHome)) { 7 | New-Item -ItemType Directory -Force -Path $karateHome 8 | } 9 | 10 | $karateFile = "${karateHome}\karate.jar"; 11 | 12 | Invoke-WebRequest -Uri $url -OutFile $karateFile -Headers @{'Host' = 'dl.bintray.com'; 'Referer' = 'https://dl.bintray.com/ptrthomas/karate/' } -UserAgent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' 13 | 14 | $karatePath = Resolve-Path -Path $karateFile 15 | 16 | $env:KARATE = $karatePath 17 | [System.Environment]::SetEnvironmentVariable('KARATE', $karatePath, [System.EnvironmentVariableTarget]::User) -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayerAggregate/IPlayersRepository.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Players.PlayerAggregate 5 | { 6 | public interface IPlayersRepository 7 | { 8 | EitherAsync Get(TenantId tenant, PlayerId id); 9 | 10 | EitherAsync Add(Player player); 11 | 12 | /// 13 | ///Persists changes to the Player 14 | /// 15 | /// Instance to persist 16 | /// Last known entity version to do concurrency check 17 | /// 18 | EitherAsync Update(Player player, Version lastKnownEntityVersion); 19 | 20 | EitherAsync Delete(TenantId tenant, PlayerId id); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/TableConfiguration/TableConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using VolleyM.Infrastructure.AzureStorage; 3 | 4 | namespace VolleyM.Infrastructure.IdentityAndAccess.AzureStorage.TableConfiguration 5 | { 6 | public class TableConfiguration : AzureTableConfiguration 7 | { 8 | private readonly IdentityContextTableStorageOptions _options; 9 | 10 | public TableConfiguration(IdentityContextTableStorageOptions options) 11 | : base(options) 12 | { 13 | _options = options; 14 | } 15 | 16 | protected override IEnumerable GetTablesForContext() 17 | { 18 | return new List 19 | { 20 | _options.UsersTable 21 | }; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/PlayerEntity.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Cosmos.Table; 2 | using VolleyM.Domain.Contracts; 3 | using VolleyM.Domain.Players.PlayerAggregate; 4 | 5 | namespace VolleyM.Infrastructure.Players.AzureStorage 6 | { 7 | public class PlayerEntity : TableEntity 8 | { 9 | 10 | public PlayerEntity() 11 | { 12 | 13 | } 14 | 15 | public PlayerEntity(TenantId tenant, PlayerId id) 16 | { 17 | PartitionKey = tenant.ToString(); 18 | RowKey = id.ToString(); 19 | } 20 | 21 | public PlayerEntity(Player player) 22 | : this(player.Tenant, player.Id) 23 | { 24 | FirstName = player.FirstName; 25 | LastName = player.LastName; 26 | } 27 | 28 | public string FirstName { get; set; } 29 | 30 | public string LastName { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Players/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:53835/", 7 | "sslPort": 44353 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "VolleyM.API.Players": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/NoOpTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using SimpleInjector; 3 | 4 | namespace VolleyM.Domain.UnitTests.Framework 5 | { 6 | public class NoOpTestFixture : ITestFixture 7 | { 8 | public void RegisterScenarioDependencies(Container container) 9 | { 10 | //do nothing 11 | } 12 | 13 | public Task ScenarioSetup() 14 | { 15 | //do nothing 16 | return Task.CompletedTask; 17 | } 18 | 19 | public Task ScenarioTearDown() 20 | { 21 | //do nothing 22 | return Task.CompletedTask; 23 | } 24 | 25 | public EntityId GetEntityId(object instance) 26 | { 27 | return null; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/HandlerStructure/ValidatorLocator.feature: -------------------------------------------------------------------------------- 1 | Feature: Validator locator 2 | See HandlerStructure.feature for more context. 3 | Validator locator is used for conditional registration of validation decorator 4 | 5 | @unit @ab:1103 6 | Scenario: Handler is not nested 7 | Given I have a handler that is not nested in a class 8 | When has validator check is performed 9 | Then false is returned 10 | 11 | @unit @ab:1103 12 | Scenario: Handler nested but no validator 13 | Given I have a handler that does not have corresponding validator 14 | When has validator check is performed 15 | Then false is returned 16 | 17 | @unit @ab:1103 18 | Scenario: Handler with validator 19 | Given I have a handler with validator 20 | When has validator check is performed 21 | Then true is returned 22 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/PlayerAggregate/Player.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | 3 | namespace VolleyM.Domain.Players.PlayerAggregate 4 | { 5 | public class Player 6 | { 7 | public Player(TenantId tenant, Version version, PlayerId id, string firstName, string lastName) 8 | { 9 | Tenant = tenant; 10 | Version = version; 11 | Id = id; 12 | FirstName = firstName; 13 | LastName = lastName; 14 | } 15 | 16 | public TenantId Tenant { get; } 17 | 18 | public Version Version { get; } 19 | 20 | public PlayerId Id { get; } 21 | 22 | public string FirstName { get; private set; } 23 | 24 | public string LastName { get; private set; } 25 | 26 | public void ChangeName(string firstName, string lastName) 27 | { 28 | this.FirstName = firstName; 29 | this.LastName = lastName; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /tests/api-tests/Contributors/hardcoded-contributors.feature: -------------------------------------------------------------------------------- 1 | Feature: Hardcoded Contributors API 2 | 3 | Background: Fetch all contributors to the Volley Management project 4 | Given url vmAppUrl 5 | 6 | Scenario: Get All contributors 7 | 8 | Given path 'api/contributors' 9 | When method GET 10 | Then status 200 11 | And assert response.length == 2 12 | And match each response == {fullName:'#string', team:'#string', courseDirection:'#string'} 13 | 14 | Scenario: Get All protected contributors 15 | 16 | Given path 'api/contributors' 17 | And header Authorization = auth_header 18 | When method GET 19 | Then status 200 20 | And assert response.length == 2 21 | And match each response == {fullName:'#string', team:'#string', courseDirection:'#string'} -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Players/PlayersApiAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Composition; 2 | using AutoMapper; 3 | using SimpleInjector; 4 | using VolleyM.Infrastructure.Bootstrap; 5 | using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; 6 | 7 | namespace VolleyM.API.Players 8 | { 9 | [Export(typeof(IAssemblyBootstrapper))] 10 | public class PlayersApiAssemblyBootstrapper : IAssemblyBootstrapper 11 | { 12 | public void RegisterDependencies(Container container, IConfiguration config) 13 | { 14 | // do nothing 15 | } 16 | 17 | public bool HasDomainComponents { get; } = false; 18 | 19 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 20 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 21 | => mce.AddProfile(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker/EventHandlerWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | 6 | namespace VolleyM.Infrastructure.EventBroker 7 | { 8 | public class EventHandlerWrapper 9 | { 10 | private readonly MethodInfo _handleMethod; 11 | 12 | public EventHandlerWrapper(Type eventType) 13 | { 14 | HandlerType = typeof(IEventHandler<>).MakeGenericType(eventType); 15 | _handleMethod = HandlerType.GetMethod(nameof(IEventHandler.Handle)); 16 | } 17 | 18 | public Type HandlerType { get; } 19 | 20 | public Task Handle(object handler, object @event) 21 | { 22 | return (Task)_handleMethod.Invoke(handler, new[] { @event }); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/CurrentUserScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using VolleyM.Domain.Framework.Authorization; 3 | 4 | namespace VolleyM.Domain.Framework 5 | { 6 | public class CurrentUserScope : IDisposable 7 | { 8 | private readonly CurrentUserProvider _currentUserProvider; 9 | 10 | private readonly CurrentUserContext _previousContext; 11 | 12 | internal CurrentUserScope(CurrentUserProvider currentUserProvider, CurrentUserContext scopedContext) 13 | { 14 | _currentUserProvider = currentUserProvider; 15 | 16 | _previousContext = currentUserProvider.Context; 17 | _currentUserProvider.Context = scopedContext; 18 | } 19 | 20 | public void Dispose() 21 | { 22 | _currentUserProvider.Context = _previousContext; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/TenantTestFixtureBase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using SimpleInjector; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.Crosscutting; 5 | 6 | namespace VolleyM.Domain.UnitTests.Framework 7 | { 8 | public abstract class TenantTestFixtureBase : ITenantTestFixture 9 | { 10 | protected Container _container; 11 | 12 | public virtual void RegisterScenarioDependencies(Container container) 13 | { 14 | _container = container; 15 | } 16 | 17 | public TenantId CurrentTenant 18 | { 19 | get 20 | { 21 | return _container.GetInstance().Tenant; 22 | } 23 | } 24 | 25 | public abstract Task ScenarioSetup(); 26 | 27 | public abstract Task ScenarioTearDown(); 28 | 29 | public abstract EntityId GetEntityId(object instance); 30 | } 31 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/Players/GetAllPlayers.feature: -------------------------------------------------------------------------------- 1 | Feature: Get All players 2 | All existing players should be listed 3 | 4 | @unit @azurecloud @ab:1021 5 | Scenario: Query all players 6 | Given several players exist 7 | | TenantId | Version | Id | FirstName | LastName | 8 | | | | player1 | Ivan | Ivanov | 9 | | | | player2 | John | Doe | 10 | | | | player3 | Marco | Polo | 11 | When I query all players 12 | Then all players are returned 13 | | Tenant | Version | Id | FirstName | LastName | 14 | | | | player1 | Ivan | Ivanov | 15 | | | | player2 | John | Doe | 16 | | | | player3 | Marco | Polo | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/AnotherApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 5 | { 6 | public class AnotherApplicationService : IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | 10 | public AnotherApplicationService(EventInvocationSpy eventSpy) 11 | { 12 | _eventSpy = eventSpy; 13 | } 14 | 15 | public Task Handle(EventC @event) 16 | { 17 | return RegisterEvent(@event); 18 | } 19 | 20 | private Task RegisterEvent(IEvent @event) 21 | { 22 | _eventSpy.RegisterInvocation(@event); 23 | return Task.CompletedTask; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextB/AnotherApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextB 5 | { 6 | public class AnotherApplicationService : IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | 10 | public AnotherApplicationService(EventInvocationSpy eventSpy) 11 | { 12 | _eventSpy = eventSpy; 13 | } 14 | 15 | public Task Handle(EventF @event) 16 | { 17 | return RegisterEvent(@event); 18 | } 19 | 20 | private Task RegisterEvent(IEvent @event) 21 | { 22 | _eventSpy.RegisterInvocation(@event); 23 | return Task.CompletedTask; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextC/AnotherApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextC 5 | { 6 | public class AnotherApplicationService : IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | 10 | public AnotherApplicationService(EventInvocationSpy eventSpy) 11 | { 12 | _eventSpy = eventSpy; 13 | } 14 | 15 | public Task Handle(EventG @event) 16 | { 17 | return RegisterEvent(@event); 18 | } 19 | 20 | private Task RegisterEvent(IEvent @event) 21 | { 22 | _eventSpy.RegisterInvocation(@event); 23 | return Task.CompletedTask; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/TwoInterfacesHandler.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.Framework.UnitTests.Fixture 5 | { 6 | public class TwoInterfacesHandler 7 | { 8 | public class Request : IRequest 9 | { 10 | public int A { get; set; } 11 | 12 | public int B { get; set; } 13 | } 14 | 15 | public class Request2 : IRequest 16 | { 17 | public int A { get; set; } 18 | 19 | public int B { get; set; } 20 | 21 | } 22 | 23 | public class Handler : IRequestHandler, IRequestHandler 24 | { 25 | public EitherAsync Handle(Request request) 26 | { 27 | return Unit.Default; 28 | } 29 | 30 | public EitherAsync Handle(Request2 request) 31 | { 32 | return Unit.Default; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/IPlayersTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Players.Handlers; 5 | using VolleyM.Domain.Players.PlayerAggregate; 6 | using VolleyM.Domain.Players.UnitTests.Fixture; 7 | using VolleyM.Domain.UnitTests.Framework; 8 | 9 | namespace VolleyM.Domain.Players.UnitTests 10 | { 11 | public interface IPlayersTestFixture : ITenantTestFixture 12 | { 13 | Task MockPlayerExists(TestPlayerDto player); 14 | 15 | Task MockSeveralPlayersExist(TenantId tenant, List testData); 16 | 17 | Task VerifyPlayerCreated(Player expectedPlayer); 18 | 19 | void MockNextRandomId(string id); 20 | 21 | Task VerifyPlayerNotCreated(Player expectedPlayer); 22 | 23 | void SetupPlayerName(IPlayerNameRequest request); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/Handlers/GetUser.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | 4 | namespace VolleyM.Domain.IdentityAndAccess.Handlers 5 | { 6 | public class GetUser 7 | { 8 | public class Request : IRequest 9 | { 10 | public UserId UserId { get; set; } 11 | 12 | public TenantId Tenant { get; set; } 13 | 14 | public override string ToString() 15 | { 16 | return $"UserId:{UserId};Tenant:{Tenant}"; 17 | } 18 | } 19 | 20 | public class Handler : IRequestHandler 21 | { 22 | private readonly IUserRepository _repository; 23 | 24 | public Handler(IUserRepository repository) 25 | { 26 | _repository = repository; 27 | } 28 | 29 | public EitherAsync Handle(Request request) 30 | { 31 | return _repository.Get(request.Tenant, request.UserId); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/UserAggregate/User.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts; 2 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 3 | 4 | namespace VolleyM.Domain.IdentityAndAccess 5 | { 6 | public class User 7 | { 8 | public User(UserId id, TenantId tenant) 9 | { 10 | Id = id; 11 | Tenant = tenant; 12 | } 13 | 14 | internal User(UserFactoryDto userDto) 15 | : this(userDto.Id, userDto.Tenant) 16 | { 17 | Role = userDto.Role; 18 | } 19 | 20 | public UserId Id { get; } 21 | 22 | public TenantId Tenant { get; } 23 | 24 | public RoleId Role { get; private set; } 25 | 26 | public bool HasRole => Role != null; 27 | 28 | public void AssignRole(RoleId role) 29 | { 30 | Role = role; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Roles/Role.feature: -------------------------------------------------------------------------------- 1 | Feature: Authorization Role 2 | Basic role class to manage group of permissions 3 | 4 | @unit @ab:1026 5 | Scenario: Add permission to a Role 6 | Given Sample role 7 | When I add PermissionA to it 8 | Then role has PermissionA 9 | 10 | @unit @ab:1026 11 | Scenario: Check not assigned permission 12 | Given Sample role 13 | When I add PermissionA to it 14 | Then role does not have PermissionB 15 | 16 | @unit @ab:1026 17 | Scenario: Add several permissions to a Role 18 | Given Sample role 19 | When I add PermissionA to it 20 | When I add PermissionB to it 21 | Then role has PermissionA 22 | Then role has PermissionB 23 | 24 | @unit @ab:1026 25 | Scenario: Add same permissions to a Role 26 | Given Sample role 27 | When I add PermissionA to it 28 | When I add PermissionA to it 29 | Then role has PermissionA -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Logging/LoggingRequestHandlerDecorator.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using Serilog; 3 | using VolleyM.Domain.Contracts; 4 | 5 | namespace VolleyM.Domain.Framework.Logging 6 | { 7 | public class LoggingRequestHandlerDecorator : DecoratorBase>, IRequestHandler 8 | where TRequest : IRequest 9 | { 10 | public LoggingRequestHandlerDecorator(IRequestHandler handler) 11 | : base(handler) { } 12 | 13 | public EitherAsync Handle(TRequest request) 14 | { 15 | var logger = Log.ForContext(RootInstance.GetType()); 16 | 17 | logger.Debug("Handling request {@Request}", request); 18 | var result = Decoratee.Handle(request) 19 | .Do(_ => logger.Debug("Request handling complete.")); 20 | 21 | return result; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/VolleyMLifestyleSelectionBehavior.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleInjector; 3 | using SimpleInjector.Advanced; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | 6 | namespace VolleyM.Domain.Framework 7 | { 8 | public class VolleyMLifestyleSelectionBehavior : ILifestyleSelectionBehavior 9 | { 10 | private readonly ContainerOptions _options; 11 | 12 | public VolleyMLifestyleSelectionBehavior(ContainerOptions options) 13 | { 14 | this._options = options; 15 | } 16 | 17 | public Lifestyle SelectLifestyle(Type implementationType) 18 | { 19 | if (implementationType.IsAssignableFrom(typeof(IEventHandler<>))) 20 | { 21 | return _options.DefaultScopedLifestyle; 22 | } 23 | 24 | return this._options.DefaultLifestyle; 25 | } 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contributors/ContributorsApiAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using SimpleInjector; 2 | using System.Composition; 3 | using AutoMapper; 4 | using VolleyM.Infrastructure.Bootstrap; 5 | 6 | namespace VolleyM.API.Contributors 7 | { 8 | [Export(typeof(IAssemblyBootstrapper))] 9 | public class ContributorsApiAssemblyBootstrapper : IAssemblyBootstrapper 10 | { 11 | public void RegisterDependencies(Container container, Microsoft.Extensions.Configuration.IConfiguration config) 12 | { 13 | // no dependencies 14 | } 15 | 16 | public bool HasDomainComponents { get; } = false; 17 | 18 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 19 | 20 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) => 21 | mce.AddProfile(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contributors/DomainContributorsAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using SimpleInjector; 2 | using System.Composition; 3 | using AutoMapper; 4 | using VolleyM.Infrastructure.Bootstrap; 5 | 6 | namespace VolleyM.Domain.Contributors 7 | { 8 | [Export(typeof(IAssemblyBootstrapper))] 9 | public class DomainContributorsAssemblyBootstrapper : IAssemblyBootstrapper 10 | { 11 | public void RegisterDependencies(Container container, Microsoft.Extensions.Configuration.IConfiguration config) 12 | { 13 | // do nothing 14 | } 15 | 16 | public bool HasDomainComponents { get; } = true; 17 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 18 | 19 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 20 | { 21 | // no mapping 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Logging/LoggingQueryObjectDecorator.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using Serilog; 3 | using VolleyM.Domain.Contracts; 4 | 5 | namespace VolleyM.Domain.Framework.Logging 6 | { 7 | public class LoggingQueryObjectDecorator : DecoratorBase>, IQuery 8 | where TResponse : class 9 | { 10 | public LoggingQueryObjectDecorator(IQuery query) : base(query) { } 11 | 12 | public EitherAsync Execute(TParam param) 13 | { 14 | var logger = Log.ForContext(RootInstance.GetType()); 15 | 16 | logger.Debug("Executing query {@QueryParam}", param); 17 | var result = Decoratee.Execute(param) 18 | .Do(_=> logger.Debug("Query execution complete.")); 19 | 20 | return result; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/DomainPlayersAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Composition; 2 | using AutoMapper; 3 | using SimpleInjector; 4 | using VolleyM.Domain.Players.PlayerAggregate; 5 | using VolleyM.Infrastructure.Bootstrap; 6 | 7 | namespace VolleyM.Domain.Players 8 | { 9 | [Export(typeof(IAssemblyBootstrapper))] 10 | public class DomainPlayersAssemblyBootstrapper : IAssemblyBootstrapper 11 | { 12 | public void RegisterDependencies(Container container, Microsoft.Extensions.Configuration.IConfiguration config) 13 | { 14 | container.Register(Lifestyle.Singleton); 15 | } 16 | 17 | public bool HasDomainComponents { get; } = true; 18 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 19 | 20 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 21 | => mce.AddProfile(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Client/VolleyM.API/CORS/CorsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace VolleyM.API.CORS 5 | { 6 | public static class CorsExtensions 7 | { 8 | public static void AddCorsFromSettings(this IServiceCollection services, CorsOptions corsOpts) 9 | { 10 | var configuredOrigins = ParseOrigins(corsOpts.AllowedOrigins); 11 | services.AddCors(opts => 12 | { 13 | opts.AddDefaultPolicy(pol => 14 | { 15 | pol.WithOrigins(configuredOrigins) 16 | .AllowAnyHeader() 17 | .AllowAnyMethod(); 18 | }); 19 | }); 20 | } 21 | 22 | private static string[] ParseOrigins(string allowedOriginsStr) 23 | => allowedOriginsStr.Split(';', StringSplitOptions.RemoveEmptyEntries); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/RolesAggregate/Role.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace VolleyM.Domain.IdentityAndAccess.RolesAggregate 4 | { 5 | public class Role 6 | { 7 | private readonly HashSet _permissions = new HashSet(); 8 | 9 | public Role(RoleId id) 10 | { 11 | Id = id; 12 | } 13 | 14 | public RoleId Id { get; } 15 | 16 | public void AddPermission(Permission permission) 17 | { 18 | _permissions.Add(GetPermissionKey(permission)); 19 | } 20 | 21 | public bool HasPermission(Permission permission) 22 | { 23 | return _permissions.TryGetValue(GetPermissionKey(permission), out var actual); 24 | } 25 | 26 | private static string GetPermissionKey(Permission permission) 27 | => permission.ToString().ToLowerInvariant(); 28 | } 29 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventAProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventAProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventA 18 | { 19 | SomeData = $"{nameof(SampleEventAProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventBProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventBProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventB 18 | { 19 | SomeData = $"{nameof(SampleEventBProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventCProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventCProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventC 18 | { 19 | SomeData = $"{nameof(SampleEventCProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventDProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventDProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventD 18 | { 19 | SomeData = $"{nameof(SampleEventDProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventEProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventEProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventE 18 | { 19 | SomeData = $"{nameof(SampleEventEProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventFProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventFProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventF 18 | { 19 | SomeData = $"{nameof(SampleEventFProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventGProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventGProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventG 18 | { 19 | SomeData = $"{nameof(SampleEventGProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventHProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventHProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventH 18 | { 19 | SomeData = $"{nameof(SampleEventHProducingHandler)} invoked", RequestData = request.EventData 20 | }; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/DecoratorBase.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Framework 2 | { 3 | public abstract class DecoratorBase 4 | { 5 | /// 6 | /// Instance wrapped by decorator, might be another decorator 7 | /// 8 | protected T Decoratee { get; } 9 | 10 | /// 11 | /// Original non-decorator instance wrapped 12 | /// 13 | protected T RootInstance 14 | { 15 | get 16 | { 17 | var result = Decoratee; 18 | 19 | while (result is DecoratorBase wrapped) 20 | { 21 | result = wrapped.Decoratee; 22 | } 23 | 24 | return result; 25 | } 26 | } 27 | 28 | protected DecoratorBase(T decoratee) 29 | { 30 | Decoratee = decoratee; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/UnitEventBrokerTestFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using SimpleInjector; 3 | using VolleyM.Domain.Framework.EventBroker; 4 | using VolleyM.Domain.UnitTests.Framework; 5 | 6 | namespace VolleyM.Infrastructure.EventBroker.UnitTests 7 | { 8 | public class UnitEventBrokerTestFixture : IEventBrokerTestFixture 9 | { 10 | public void RegisterScenarioDependencies(Container container) 11 | { 12 | container.Register(); 13 | } 14 | 15 | public Task ScenarioSetup() 16 | { 17 | return Task.CompletedTask; 18 | } 19 | 20 | public Task ScenarioTearDown() 21 | { 22 | return Task.CompletedTask; 23 | } 24 | 25 | public EntityId GetEntityId(object instance) 26 | { 27 | return null; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Players/Handlers/GetAll.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.Crosscutting; 5 | 6 | namespace VolleyM.Domain.Players.Handlers 7 | { 8 | using IQueryObject = IQuery>; 9 | 10 | public class GetAll 11 | { 12 | public class Request : IRequest> 13 | { 14 | // no params 15 | } 16 | 17 | public class Handler : IRequestHandler> 18 | { 19 | private readonly IQueryObject _query; 20 | private readonly ICurrentUserProvider _currentUser; 21 | 22 | public Handler(IQueryObject query, ICurrentUserProvider currentUser) 23 | { 24 | _query = query; 25 | _currentUser = currentUser; 26 | } 27 | 28 | public EitherAsync> Handle(Request request) 29 | { 30 | return _query.Execute(_currentUser.Tenant); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/AnotherEventAProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class AnotherEventAProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventA 18 | { 19 | SomeData = $"{nameof(AnotherEventAProducingHandler)} invoked", 20 | RequestData = request.EventData 21 | }; 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/DomainIdentityAndAccessAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using SimpleInjector; 2 | using System.Composition; 3 | using AutoMapper; 4 | using VolleyM.Infrastructure.Bootstrap; 5 | 6 | namespace VolleyM.Domain.IdentityAndAccess 7 | { 8 | [Export(typeof(IAssemblyBootstrapper))] 9 | public class DomainIdentityAndAccessAssemblyBootstrapper : IAssemblyBootstrapper 10 | { 11 | public void RegisterDependencies(Container container, Microsoft.Extensions.Configuration.IConfiguration config) 12 | { 13 | container.Register(Lifestyle.Singleton); 14 | } 15 | 16 | public bool HasDomainComponents { get; } = true; 17 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 18 | 19 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 20 | { 21 | // no mapping 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/IDomainFrameworkTestFixture.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.IdentityAndAccess; 2 | using VolleyM.Domain.IdentityAndAccess.Handlers; 3 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 4 | using VolleyM.Domain.UnitTests.Framework; 5 | 6 | namespace VolleyM.Domain.Framework.UnitTests 7 | { 8 | public interface IDomainFrameworkTestFixture : ITestFixture 9 | { 10 | void VerifyUserNotCreated(); 11 | void VerifyUserCreated(CreateUser.Request expectedRequest); 12 | void MockCreateUserSuccess(); 13 | void MockCreateUserError(); 14 | void MockUserExists(User user); 15 | void MockUserNotFound(); 16 | void MockGetUserError(); 17 | void SetCurrentUser(User currentUser); 18 | User CreateAUser(); 19 | void MockRoleStoreError(); 20 | void SetupRole(Role role); 21 | void MockHostingEnvironmentIsProduction(bool isProduction); 22 | } 23 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Transforms/SpecFlowTransformFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace VolleyM.Domain.UnitTests.Framework 6 | { 7 | internal class SpecFlowTransformFactory : ISpecFlowTransformFactory 8 | { 9 | private readonly Dictionary _registry; 10 | 11 | private readonly ISpecFlowTransform _empty = new NoOpSpecFlowTransform(); 12 | 13 | public SpecFlowTransformFactory(IEnumerable transforms) 14 | { 15 | _registry = transforms.ToDictionary(t => t.TargetType, t => t); 16 | } 17 | 18 | public ISpecFlowTransform GetTransform(Type targetType) 19 | { 20 | if (!_registry.TryGetValue(targetType, out var result)) 21 | { 22 | result = _empty; 23 | } 24 | 25 | return result; 26 | } 27 | 28 | public void RegisterTransform(ISpecFlowTransform transform) 29 | { 30 | // do nothing 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/ValidationDecorator/ValidationDecorator.feature: -------------------------------------------------------------------------------- 1 | Feature: Validation Decorator 2 | 3 | As a developer I want a sub-system that will automatically hookup up validation logic 4 | based on presense of some interface, e.g. IValidator 5 | so Validators can be added by creating validator implementation 6 | 7 | @unit @ab:1103 8 | Scenario: No validator exists 9 | Given I have a handler 10 | And validator is not defined 11 | When I call decorated handler 12 | Then handler result should be returned 13 | 14 | @unit @ab:1103 15 | Scenario: Validator success 16 | Given I have a handler 17 | And single validator defined 18 | And validator passes 19 | When I call decorated handler 20 | Then handler result should be returned 21 | 22 | @unit @ab:1103 23 | Scenario: Validator failed 24 | Given I have a handler 25 | And single validator defined 26 | And validator fails 27 | When I call decorated handler 28 | Then validation error should be returned -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Bootstrap/IAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using SimpleInjector; 3 | using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; 4 | 5 | namespace VolleyM.Infrastructure.Bootstrap 6 | { 7 | /// 8 | /// Used by assemblies to register themselves in the application 9 | /// 10 | public interface IAssemblyBootstrapper 11 | { 12 | void RegisterDependencies(Container container, IConfiguration config); 13 | 14 | /// 15 | /// Indicates whether this assembly is a target for common registrations 16 | /// 17 | bool HasDomainComponents { get; } 18 | 19 | /// 20 | /// Provides registrar for common dependencies; or null 21 | /// 22 | IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } 23 | 24 | void RegisterMappingProfiles(MapperConfigurationExpression mce); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleEventIProducingHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class SampleEventIProducingHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | protected override IEvent GetEvent(IEventProducingRequest request) 16 | { 17 | return new EventI 18 | { 19 | SomeData = $"{nameof(SampleEventIProducingHandler)} invoked", 20 | RequestData = request.EventData, 21 | IgnoredProperty = "irrelevant data" 22 | }; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/SampleHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentValidation; 3 | using LanguageExt; 4 | using VolleyM.Domain.Contracts; 5 | using VolleyM.Domain.Contracts.EventBroker; 6 | 7 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 8 | { 9 | public class SampleHandler 10 | { 11 | public class Request : IRequest 12 | { 13 | public int A { get; set; } 14 | 15 | public int B { get; set; } 16 | 17 | } 18 | 19 | public class Validator : AbstractValidator 20 | { 21 | public Validator() 22 | { 23 | RuleFor(r => r.A).Equal(0); 24 | } 25 | } 26 | 27 | public class Handler : IRequestHandler, ICanProduceEvent 28 | { 29 | public EitherAsync Handle(Request request) 30 | { 31 | DomainEvents.Add(new SampleEvent { Data = "SampleHandler invoked" }); 32 | return Unit.Default; 33 | } 34 | 35 | public List DomainEvents { get; } = new List(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Users/CreateUser.feature: -------------------------------------------------------------------------------- 1 | Feature: Create User 2 | User will be enrolled into a system by Auth0 3 | And Auth0 will store most of authentication data 4 | But we still need a minimal feature to have users referenced in authorization policies 5 | 6 | @unit @azurecloud @ab:1026 7 | Scenario: Create new user 8 | Given UserId provided 9 | And Tenant provided 10 | And Role provided 11 | And user does not exist 12 | When I execute CreateUser 13 | Then user is created 14 | And user is returned 15 | 16 | @unit @azurecloud @ab:1026 17 | Scenario: Create existing user 18 | Given UserId provided 19 | And Tenant provided 20 | And such user already exists 21 | When I execute CreateUser 22 | Then Conflict error is returned 23 | 24 | @unit @azurecloud @ab:1026 25 | Scenario: Create new without Role 26 | Given UserId provided 27 | And Tenant provided 28 | And user does not exist 29 | When I execute CreateUser 30 | Then user is created 31 | And user is returned -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextC/SamplePublicApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextC 5 | { 6 | public class SamplePublicApplicationService : IEventHandler, IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | 10 | public SamplePublicApplicationService(EventInvocationSpy eventSpy) 11 | { 12 | _eventSpy = eventSpy; 13 | } 14 | 15 | public Task Handle(EventG @event) 16 | { 17 | return RegisterEvent(@event); 18 | } 19 | 20 | public Task Handle(EventI @event) 21 | { 22 | return RegisterEvent(@event); 23 | } 24 | 25 | private Task RegisterEvent(IEvent @event) 26 | { 27 | _eventSpy.RegisterInvocation(@event); 28 | return Task.CompletedTask; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /docs/adr/0005-use-azure-app-services-for-hosting.md: -------------------------------------------------------------------------------- 1 | # Use Azure App Services for deploying VolleyManagement.API container 2 | 3 | * Status: ✔ accepted 4 | * Deciders: Sergii Diachenko 5 | * Date: 2019-08-23 6 | 7 | ## Context and Problem Statement 8 | 9 | We need a hosting provider to host application. It should be cloud capable of hosting Docker containers. 10 | 11 | ## Decision Drivers 12 | 13 | * cost 14 | * ease of deployment 15 | * custom domains 16 | * https 17 | 18 | ## Considered Options 19 | 20 | * Azure App Service 21 | 22 | ## Decision Outcome 23 | 24 | Chosen option: "Azure App Service". 25 | 26 | Honestly this was chosen because I had experience running VolleyManagement V2 on Azure App Service. Price is acceptable and I did not have time to evaluate other options I'd want: 27 | 28 | * Digital Ocean 29 | * GCP App Engine Flexible environment 30 | 31 | ## Links 32 | 33 | * Depends on [ADR-0004](0004-use-docker-for-hosting-application.md) 34 | * Influences [ADR-0006](0006-use-azure-table-storage-for-persistence.md) 35 | -------------------------------------------------------------------------------- /karate-tests.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "tests\\api-tests" 5 | } 6 | ], 7 | "settings": { 8 | "workbench.colorCustomizations": { 9 | "activityBar.background": "#2da8d9", 10 | "activityBar.activeBackground": "#2da8d9", 11 | "activityBar.activeBorder": "#bd2291", 12 | "activityBar.foreground": "#15202b", 13 | "activityBar.inactiveForeground": "#15202b99", 14 | "activityBarBadge.background": "#bd2291", 15 | "activityBarBadge.foreground": "#e7e7e7", 16 | "titleBar.activeBackground": "#2089b3", 17 | "titleBar.inactiveBackground": "#2089b399", 18 | "titleBar.activeForeground": "#e7e7e7", 19 | "titleBar.inactiveForeground": "#e7e7e799", 20 | "statusBar.background": "#2089b3", 21 | "statusBarItem.hoverBackground": "#2da8d9", 22 | "statusBar.foreground": "#e7e7e7" 23 | }, 24 | "peacock.color": "#2089b3", 25 | "karateRunner.karateJar.commandLineArgs": "java -cp ${workspaceRoot}\\karate.jar com.intuit.karate.Main -e dev", 26 | "karateRunner.tests.toTarget": "**/*.feature" 27 | } 28 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker.MassTransit/EventBrokerMassTransitAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper.Configuration; 2 | using SimpleInjector; 3 | using System.Composition; 4 | using VolleyM.Infrastructure.Bootstrap; 5 | using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; 6 | 7 | namespace VolleyM.Infrastructure.EventBroker.MassTransit 8 | { 9 | [Export(typeof(IAssemblyBootstrapper))] 10 | public class EventBrokerMassTransitAssemblyBootstrapper : IAssemblyBootstrapper 11 | { 12 | public void RegisterDependencies(Container container, IConfiguration config) 13 | { 14 | container.Register(Lifestyle.Singleton); 15 | container.RegisterInitializer(mtep => mtep.StartBus().RunSynchronously()); 16 | } 17 | 18 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 19 | { 20 | // do nothing 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /tests/api-tests/Players/players.feature: -------------------------------------------------------------------------------- 1 | Feature: Players API 2 | 3 | Background: 4 | Given url vmAppUrl 5 | 6 | Scenario: Create Player 7 | Given path 'api/players' 8 | And header Authorization = auth_header 9 | And request {'firstName': 'Ivan', 'lastName': 'Petrov'} 10 | And method POST 11 | Then status 200 12 | And match response == {tenant: '#string', 'id':'#string', 'firstName': 'Ivan', 'lastName': 'Petrov'} 13 | 14 | Scenario: Create another player 15 | Given path 'api/players' 16 | And header Authorization = auth_header 17 | And request {'firstName': 'John', 'lastName': 'Smith'} 18 | And method POST 19 | Then status 200 20 | 21 | Scenario: List players 22 | Given path 'api/players' 23 | And header Authorization = auth_header 24 | And method GET 25 | Then status 200 26 | And match response == '#[2]' 27 | And match response contains {tenant: '#string', 'id':'#string', 'firstName': 'Ivan', 'lastName': 'Petrov'} 28 | And match response contains {tenant: '#string', 'id':'#string', 'firstName': 'John', 'lastName': 'Smith'} -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Authentication/AuthenticationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Authentication.JwtBearer; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace VolleyM.API.Authentication 6 | { 7 | public static class AuthenticationExtensions 8 | { 9 | public static AuthenticationBuilder UseJwtAuth(this IServiceCollection services) => 10 | services.AddAuthentication(options => 11 | { 12 | options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 13 | options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 14 | 15 | }); 16 | 17 | public static AuthenticationBuilder AddAuth0JwtBearer(this AuthenticationBuilder builder, Auth0Options opts) => 18 | builder.AddJwtBearer(options => 19 | { 20 | options.Authority = $"https://{opts.Domain}/"; 21 | options.Audience = opts.ApiIdentifier; 22 | }); 23 | } 24 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Authorization/AuthorizationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication.JwtBearer; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using SimpleInjector; 5 | 6 | namespace VolleyM.API.Authorization 7 | { 8 | public static class AuthorizationExtensions 9 | { 10 | public static IServiceCollection AddDefaultVolleyMAuthorization(this IServiceCollection services, Container container) 11 | { 12 | services.AddAuthorizationCore(config => 13 | { 14 | config.DefaultPolicy = new AuthorizationPolicyBuilder() 15 | .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) 16 | .AddRequirements(new DefaultVolleyMAuthorizationRequirement()) 17 | .Build(); 18 | }); 19 | 20 | services.AddScoped(service => new VolleyMDefaultAuthorizationHandler(container)); 21 | 22 | return services; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Authorization/VolleyMDefaultAuthorizationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Authorization; 3 | using SimpleInjector; 4 | 5 | namespace VolleyM.API.Authorization 6 | { 7 | public class VolleyMDefaultAuthorizationHandler : AuthorizationHandler 8 | { 9 | private readonly Container _container; 10 | 11 | public VolleyMDefaultAuthorizationHandler(Container container) 12 | { 13 | _container = container; 14 | } 15 | 16 | protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DefaultVolleyMAuthorizationRequirement requirement) 17 | { 18 | var authZHandler = _container.GetInstance(); 19 | 20 | return authZHandler.AuthorizeUser(context.User) 21 | .Match( 22 | _ => context.Succeed(requirement), 23 | _ => context.Fail() 24 | ); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker/AsyncScopedEventHandlerProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using SimpleInjector; 4 | using SimpleInjector.Lifestyles; 5 | using VolleyM.Domain.Contracts.EventBroker; 6 | 7 | namespace VolleyM.Infrastructure.EventBroker 8 | { 9 | public class AsyncScopedEventHandlerProxy : IEventHandler where T : IEvent 10 | { 11 | private readonly Container _container; 12 | private readonly Func> _decorateeFactory; 13 | 14 | public AsyncScopedEventHandlerProxy(Func> decorateeFactory, Container container) 15 | { 16 | _decorateeFactory = decorateeFactory; 17 | _container = container; 18 | } 19 | 20 | public Task Handle(T @event) 21 | { 22 | using (AsyncScopedLifestyle.BeginScope(_container)) 23 | { 24 | var handler = _decorateeFactory.Invoke(); 25 | return handler.Handle(@event); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/TestFixture/ITestFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.Configuration; 3 | using SimpleInjector; 4 | 5 | namespace VolleyM.Domain.UnitTests.Framework 6 | { 7 | /// 8 | /// Designates a fixture used to setup test state and manage test data 9 | /// 10 | public interface ITestFixture 11 | { 12 | void RegisterScenarioDependencies(Container container); 13 | 14 | Task ScenarioSetup(); 15 | 16 | Task ScenarioTearDown(); 17 | 18 | /// 19 | /// Domain Entities doe snot require aggregated Id, but for tests it's useful to aggregate those into single one. 20 | /// 21 | /// Domain instance 22 | /// Id or null 23 | EntityId GetEntityId(object instance); 24 | } 25 | 26 | public interface IOneTimeTestFixture 27 | { 28 | void OneTimeSetup(IConfiguration configuration); 29 | 30 | void OneTimeTearDown(); 31 | } 32 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Fixture/SeveralEventsHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | 6 | namespace VolleyM.Domain.IDomainFrameworkTestFixture 7 | { 8 | public class SeveralEventsHandler 9 | { 10 | public class Request : IRequest 11 | { 12 | public int A { get; set; } 13 | 14 | public int B { get; set; } 15 | 16 | } 17 | 18 | public class SampleEventA: IEvent 19 | { 20 | public string Data { get; set; } 21 | } 22 | 23 | public class SampleEventB: IEvent 24 | { 25 | public string Data { get; set; } 26 | } 27 | 28 | public class Handler : IRequestHandler, ICanProduceEvent 29 | { 30 | public EitherAsync Handle(Request request) 31 | { 32 | DomainEvents.Add(new SampleEventA()); 33 | DomainEvents.Add(new SampleEventB()); 34 | return Unit.Default; 35 | } 36 | 37 | public List DomainEvents { get; } = new List(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/EventProducingHandlerBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | 6 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture 7 | { 8 | public class EventProducingHandlerBase 9 | { 10 | public abstract class Request : IRequest, IEventProducingRequest 11 | { 12 | public int EventData { get; set; } 13 | } 14 | 15 | public abstract class Handler : IRequestHandler, ICanProduceEvent 16 | where T:Request 17 | { 18 | public EitherAsync Handle(T request) 19 | { 20 | DomainEvents.Add(GetEvent(request)); 21 | return Unit.Default; 22 | } 23 | 24 | protected abstract IEvent GetEvent(IEventProducingRequest request); 25 | 26 | public List DomainEvents { get; } = new List(); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/ScopeAwareApplicationService1.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 5 | { 6 | public class ScopeAwareApplicationService1 : IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | private readonly ScopeLitmus _scopeLitmus; 10 | 11 | public ScopeAwareApplicationService1(EventInvocationSpy eventSpy, ScopeLitmus scopeLitmus) 12 | { 13 | _eventSpy = eventSpy; 14 | _scopeLitmus = scopeLitmus; 15 | } 16 | 17 | public Task Handle(EventJ @event) 18 | { 19 | @event.EventHandlerScope = _scopeLitmus.Scope; 20 | return RegisterEvent(@event); 21 | } 22 | 23 | private Task RegisterEvent(IEvent @event) 24 | { 25 | _eventSpy.RegisterInvocation(@event); 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/ScopeAwareApplicationService2.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 5 | { 6 | public class ScopeAwareApplicationService2 : IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | private readonly ScopeLitmus _scopeLitmus; 10 | 11 | public ScopeAwareApplicationService2(EventInvocationSpy eventSpy, ScopeLitmus scopeLitmus) 12 | { 13 | _eventSpy = eventSpy; 14 | _scopeLitmus = scopeLitmus; 15 | } 16 | 17 | public Task Handle(EventJ @event) 18 | { 19 | @event.EventHandlerScope = _scopeLitmus.Scope; 20 | return RegisterEvent(@event); 21 | } 22 | 23 | private Task RegisterEvent(IEvent @event) 24 | { 25 | _eventSpy.RegisterInvocation(@event); 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Contributors/ContributorsController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using AutoMapper; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Serilog; 6 | using VolleyM.API.Contracts; 7 | using VolleyM.Domain.Contracts; 8 | using VolleyM.Domain.Contributors; 9 | 10 | namespace VolleyM.API.Contributors 11 | { 12 | [Route("api/[controller]")] 13 | [ApiController] 14 | public class ContributorsController : ControllerBase 15 | { 16 | private readonly IRequestHandler> _handler; 17 | private readonly IMapper _mapper; 18 | 19 | public ContributorsController(IRequestHandler> handler, IMapper mapper) 20 | { 21 | _handler = handler; 22 | _mapper = mapper; 23 | } 24 | 25 | [HttpGet] 26 | [Route("")] 27 | public Task GetAll() 28 | { 29 | Log.Information("Controller {Action} action called.", nameof(GetAll)); 30 | 31 | return _handler.ExecuteHandler(new GetAll.Request(), _mapper.Map>); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/Extensions/AppPartsExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ApplicationParts; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Linq; 5 | using Serilog; 6 | using VolleyM.Infrastructure.Bootstrap; 7 | 8 | namespace VolleyM.API.Extensions 9 | { 10 | internal static class AppPartsExtensions 11 | { 12 | internal static IMvcBuilder AddVolleyManagementApiParts(this IMvcBuilder mvcBuilder, AssemblyBootstrapper assemblyBootstrapper, string assemblyPrefix = "VolleyM.API.") 13 | { 14 | var pluginAssemblies = assemblyBootstrapper.DiscoveredAssemblies 15 | .Where(s => s.FullName.StartsWith(assemblyPrefix, StringComparison.OrdinalIgnoreCase)) 16 | .Select(a => new AssemblyPart(a)) 17 | .ToList(); 18 | 19 | Log.Information("API: {APIParts} Parts discovered.", pluginAssemblies.Count); 20 | 21 | return mvcBuilder.ConfigureApplicationPartManager(apm => pluginAssemblies.ForEach(apm.ApplicationParts.Add)); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/UserEntity.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Cosmos.Table; 2 | using VolleyM.Domain.Contracts; 3 | using VolleyM.Domain.IdentityAndAccess; 4 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 5 | 6 | namespace VolleyM.Infrastructure.IdentityAndAccess.AzureStorage 7 | { 8 | public class UserEntity : TableEntity 9 | { 10 | public string RoleId { get; set; } 11 | 12 | public UserEntity() 13 | { 14 | 15 | } 16 | 17 | public UserEntity(User user) 18 | : this(user.Tenant, user.Id, user.Role) 19 | { 20 | } 21 | 22 | public UserEntity(TenantId tenant, UserId id) 23 | { 24 | PartitionKey = tenant.ToString(); 25 | RowKey = id.ToString(); 26 | } 27 | 28 | public UserEntity(TenantId tenant, UserId id, RoleId role) 29 | { 30 | PartitionKey = tenant.ToString(); 31 | RowKey = id.ToString(); 32 | 33 | RoleId = role?.ToString(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /docs/tournaments/lviv-it-league-2018.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Volleyball IT - League Lviv 8 | 9 | 10 | Find latest news and updates at Facebook page 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.IdentityAndAccess/Handlers/CreateUser.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using VolleyM.Domain.Contracts; 3 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 4 | 5 | namespace VolleyM.Domain.IdentityAndAccess.Handlers 6 | { 7 | public class CreateUser 8 | { 9 | public class Request : IRequest 10 | { 11 | public UserId UserId { get; set; } 12 | 13 | public TenantId Tenant { get; set; } 14 | 15 | public RoleId Role { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | return $"UserId:{UserId};Tenant:{Tenant}"; 20 | } 21 | } 22 | 23 | public class Handler : IRequestHandler 24 | { 25 | private readonly IUserRepository _repository; 26 | 27 | public Handler(IUserRepository repository) 28 | { 29 | _repository = repository; 30 | } 31 | 32 | public EitherAsync Handle(Request request) 33 | { 34 | var user = new User(request.UserId, request.Tenant); 35 | if (request.Role != null) 36 | { 37 | user.AssignRole(request.Role); 38 | } 39 | 40 | return _repository.Add(user); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/Validation/ValidationHandlerDecorator.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | 5 | namespace VolleyM.Domain.Framework.Validation 6 | { 7 | public class ValidationHandlerDecorator 8 | : DecoratorBase>, IRequestHandler 9 | where TRequest : IRequest 10 | { 11 | private readonly IValidator _validator; 12 | 13 | public ValidationHandlerDecorator(IRequestHandler decoratee, IValidator validator) 14 | : base(decoratee) 15 | { 16 | _validator = validator; 17 | } 18 | 19 | public EitherAsync Handle(TRequest request) 20 | { 21 | var validationResult = _validator.Validate(request); 22 | 23 | if (!validationResult.IsValid) 24 | { 25 | return new ValidationError(validationResult); 26 | } 27 | 28 | return base.Decoratee.Handle(request); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Volley Management System 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Transforms/Common/TenantIdTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Serilog; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Contracts.Crosscutting; 5 | 6 | namespace VolleyM.Domain.UnitTests.Framework.Common 7 | { 8 | public class TenantIdTransform : ISpecFlowTransform 9 | { 10 | private readonly ICurrentUserProvider _currentUserProvider; 11 | 12 | public TenantIdTransform(ICurrentUserProvider currentUserProvider) 13 | { 14 | _currentUserProvider = currentUserProvider; 15 | } 16 | 17 | public Type TargetType { get; } = typeof(TenantId); 18 | 19 | public object GetValue(object instance, string rawValue) 20 | { 21 | if (string.IsNullOrEmpty(rawValue)) 22 | { 23 | return null; 24 | } 25 | 26 | return rawValue == "" 27 | ? GetCurrentTenant() 28 | : new TenantId(rawValue); 29 | } 30 | 31 | private TenantId GetCurrentTenant() 32 | { 33 | try 34 | { 35 | return _currentUserProvider.Tenant; 36 | } 37 | catch (Exception e) 38 | { 39 | Log.Error(e, "Failed to get Current tenant"); 40 | throw; 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API/FeatureManagement/ApiFeatureManager.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Esquio.Abstractions; 4 | using SimpleInjector; 5 | using VolleyM.Domain.Contracts.FeatureManagement; 6 | 7 | namespace VolleyM.API.FeatureManagement 8 | { 9 | public class ApiFeatureManager : IFeatureManager 10 | { 11 | private readonly Container _container; 12 | 13 | public ApiFeatureManager(Container container) 14 | { 15 | _container = container; 16 | } 17 | 18 | public Task IsEnabledAsync(string featureName, string contextName, 19 | CancellationToken cancellationToken = default(CancellationToken)) 20 | { 21 | // This is a workaround 22 | // When SimpleInjector runs Verify method it creates all instances of the services to resolve the tree. 23 | // Esquio depends on IHttpContextAccessor which is not available outside of active request scope 24 | // Verify() methods runs at a startup so there is a clash :( 25 | var featureService = _container.GetInstance(); 26 | 27 | return featureService.IsEnabledAsync($"{contextName}::{featureName}", cancellationToken); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/Transforms/Common/Version/VersionEquivalencyStep.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FluentAssertions.Equivalency; 3 | using VolleyM.Domain.Contracts; 4 | 5 | namespace VolleyM.Domain.UnitTests.Framework.Transforms.Common 6 | { 7 | public class VersionEquivalencyStep : IEquivalencyStep 8 | { 9 | public bool CanHandle(IEquivalencyValidationContext context, 10 | IEquivalencyAssertionOptions config) 11 | { 12 | return context.RuntimeType == typeof(Version); 13 | } 14 | 15 | public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator 16 | structuralEqualityValidator, IEquivalencyAssertionOptions config) 17 | { 18 | var version = context.Subject as Version; 19 | var versionString = context.Expectation?.ToString(); 20 | if (versionString == "" && versionString != Version.Initial.ToString()) 21 | { 22 | version.Should().NotBeNull(context.Because, context.BecauseArgs); 23 | } 24 | else 25 | { 26 | context.Subject.Should().Be(context.Expectation, context.Because, context.BecauseArgs); 27 | } 28 | 29 | return true; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ScopeLitmus.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture 4 | { 5 | /// 6 | /// Will provide unique string for each unique instance it was created 7 | /// 8 | public class ScopeLitmus 9 | { 10 | private static IEnumerable GetScopes() 11 | { 12 | yield return "rootScope"; 13 | 14 | for (int i = 0; i < 100; i++) 15 | { 16 | yield return $"eventScope{i + 1}"; 17 | } 18 | } 19 | 20 | private static IEnumerator _scopes; 21 | 22 | static ScopeLitmus() 23 | { 24 | RestartCounter(); 25 | } 26 | 27 | public static void RestartCounter() 28 | { 29 | _scopes = GetScopes().GetEnumerator(); 30 | _scopes.MoveNext(); 31 | } 32 | 33 | public ScopeLitmus() 34 | { 35 | Scope = _scopes.Current; 36 | _scopes.MoveNext(); 37 | } 38 | 39 | public string Scope { get; } 40 | } 41 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/Fixture/AzureCloudPlayersOneTimeFixture.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.Configuration; 3 | using VolleyM.Domain.UnitTests.Framework; 4 | using VolleyM.Infrastructure.Players.AzureStorage.TableConfiguration; 5 | 6 | namespace VolleyM.Domain.Players.UnitTests.Fixture 7 | { 8 | public class AzureCloudPlayersOneTimeFixture : IOneTimeTestFixture 9 | { 10 | private TableConfiguration _tableConfig; 11 | private PlayersContextTableStorageOptions _options; 12 | 13 | public void OneTimeSetup(IConfiguration configuration) 14 | { 15 | _options = configuration.GetSection("PlayersContextTableStorageOptions") 16 | .Get(); 17 | 18 | _tableConfig = new TableConfiguration(_options); 19 | var result = _tableConfig.ConfigureTables().ToEither().Result; 20 | 21 | result.IsRight.Should().BeTrue("Azure Storage should be configured correctly"); 22 | } 23 | 24 | public void OneTimeTearDown() 25 | { 26 | var result = _tableConfig.CleanTables().ToEither().Result; 27 | result.IsRight.Should().BeTrue("Azure Storage should be cleaned up correctly"); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Hardcoded/InfrastructureHardcodedAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using SimpleInjector; 2 | using System.Composition; 3 | using System.Reflection; 4 | using AutoMapper; 5 | using VolleyM.Domain.Contracts; 6 | using VolleyM.Domain.Framework.Authorization; 7 | using VolleyM.Infrastructure.Bootstrap; 8 | 9 | namespace VolleyM.Infrastructure.Hardcoded 10 | { 11 | [Export(typeof(IAssemblyBootstrapper))] 12 | public class InfrastructureHardcodedAssemblyBootstrapper : IAssemblyBootstrapper 13 | { 14 | public void RegisterDependencies(Container container, Microsoft.Extensions.Configuration.IConfiguration config) 15 | { 16 | container.Register(Lifestyle.Scoped); 17 | 18 | container.Register(typeof(IQuery<,>), Assembly.GetAssembly(GetType()), Lifestyle.Scoped); 19 | } 20 | 21 | public bool HasDomainComponents { get; } = false; 22 | 23 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 24 | 25 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 26 | { 27 | // no mapping 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/ScopeAwareRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using VolleyM.Domain.Contracts.EventBroker; 2 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture; 3 | using VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA; 4 | 5 | namespace VolleyM.Domain.ContextA 6 | { 7 | public class ScopeAwareRequestHandler 8 | { 9 | public class Request : EventProducingHandlerBase.Request 10 | { 11 | } 12 | 13 | public class Handler : EventProducingHandlerBase.Handler 14 | { 15 | private readonly ScopeLitmus _scopeLitmus; 16 | 17 | public Handler(ScopeLitmus scopeLitmus) 18 | { 19 | _scopeLitmus = scopeLitmus; 20 | } 21 | 22 | protected override IEvent GetEvent(IEventProducingRequest request) 23 | { 24 | return new EventJ 25 | { 26 | SomeData = $"{nameof(ScopeAwareRequestHandler)} invoked", 27 | RequestData = request.EventData, 28 | RequestScope = _scopeLitmus.Scope 29 | }; 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /docs/adr/0002-use-sendgrid-as-mailing-system.md: -------------------------------------------------------------------------------- 1 | # Use SendGrid for sending emails 2 | 3 | * Status: ✔ accepted 4 | * Deciders: Sergii Diachenko 5 | * Date: 2019-08-22 6 | 7 | Technical Story: [Email Notifications Feature](https://dev.azure.com/VolleyManagement/%CE%94%CE%B9%CE%B1%CF%87%CE%B5%CE%B9%CF%81%CE%B9%CF%80%CE%B7/_workitems/edit/1003) 8 | 9 | ## Context and Problem Statement 10 | 11 | We need a way to send emails to users for several workflows. 12 | 13 | ## Considered Options 14 | 15 | * SendGrid 16 | * GMail 17 | 18 | ## Decision Outcome 19 | 20 | Chosen option: SendGrid, because it is SaaS and has generous free tier. Integration is easy. 21 | 22 | ### Positive Consequences 23 | 24 | * Fast time to develop 25 | 26 | ### Negative Consequences 27 | 28 | * We need to think about setting up development environment 29 | 30 | ## Pros and Cons of the Options 31 | 32 | ### SendGrid 33 | 34 | * ✔ has free tier 35 | * ✔ has mail customization and templates 36 | * ✔ fast integration 37 | * ❌ hard to say 38 | 39 | ### GMail 40 | 41 | You can use GMail servers to send emails from your own email 42 | 43 | * ✔ free to use with some limitations 44 | * ❌ emails will be coming from GMail address always 45 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/VolleyM.Infrastructure.Players.AzureStorage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Framework/FrameworkDomainComponentDependencyRegistrar.cs: -------------------------------------------------------------------------------- 1 | using FluentValidation; 2 | using SimpleInjector; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using VolleyM.Domain.Contracts; 6 | using VolleyM.Domain.Contracts.EventBroker; 7 | using VolleyM.Infrastructure.Bootstrap; 8 | 9 | namespace VolleyM.Domain.Framework 10 | { 11 | public class FrameworkDomainComponentDependencyRegistrar : IDomainComponentDependencyRegistrar 12 | { 13 | public void RegisterCommonDependencies(Container container, List domainComponentsAssemblies) 14 | { 15 | RegisterCommonServices(container, domainComponentsAssemblies); 16 | } 17 | 18 | public static void RegisterCommonServices(Container container, List domainComponentsAssemblies) 19 | { 20 | container.Register(typeof(IRequestHandler<,>), domainComponentsAssemblies, Lifestyle.Scoped); 21 | 22 | container.Collection.Register(typeof(IEventHandler<>), domainComponentsAssemblies); 23 | container.Collection.Register(typeof(IEventHandler), domainComponentsAssemblies); 24 | 25 | container.Register(typeof(IValidator<>), domainComponentsAssemblies, Lifestyle.Scoped); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /docs/adr/0003-use-angular-for-spa.md: -------------------------------------------------------------------------------- 1 | # Use Angular Framework for VolleyManagement.SPA container 2 | 3 | * Status: ✔ accepted 4 | * Deciders: Sergii Diachenko 5 | * Date: 2019-08-23 6 | 7 | ## Context and Problem Statement 8 | 9 | VolleyManagement.SPA provides a way for users to interact. We need to select a framework which will be used to develop. 10 | 11 | ## Decision Drivers 12 | 13 | * TypeScript support 14 | * Unit Test support 15 | * Skill availability 16 | * UI components availability 17 | 18 | ## Considered Options 19 | 20 | * Angular 21 | * Vue.JS 22 | 23 | ## Decision Outcome 24 | 25 | Chosen option: Angular, because skill availability is better. 26 | 27 | ### Positive Consequences 28 | 29 | * Easier to get work done as it will be easier to find person eager to contribute 30 | 31 | ### Negative Consequences 32 | 33 | * Angular has a reputation of being complex. As a workaround we can always build Vue.JS app later 34 | 35 | ## Pros and Cons of the Options 36 | 37 | ### Angular 38 | 39 | Latest Angular version 40 | 41 | * ✔ TypeScript support 42 | * ✔ Unit Test support 43 | * ✔ Skill availability 44 | * ✔ UI components availability 45 | 46 | ### Vue.js 47 | 48 | * ✔ TypeScript support 49 | * ✔ Unit Test support 50 | * ❌ Skill availability 51 | * ❓ UI components availability 52 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Contributors.UnitTests/UnitContributorsTestFixture.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute; 2 | using SimpleInjector; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using LanguageExt; 6 | using VolleyM.Domain.Contracts; 7 | using VolleyM.Domain.UnitTests.Framework; 8 | 9 | namespace VolleyM.Domain.Contributors.UnitTests 10 | { 11 | public class UnitContributorsTestFixture : IContributorsTestFixture 12 | { 13 | private IQuery> _queryMock; 14 | 15 | public void RegisterScenarioDependencies(Container container) 16 | { 17 | _queryMock = Substitute.For>>(); 18 | 19 | container.Register(() => _queryMock, Lifestyle.Scoped); 20 | } 21 | 22 | public Task ScenarioSetup() 23 | { 24 | return Task.CompletedTask; 25 | } 26 | 27 | public Task ScenarioTearDown() 28 | { 29 | return Task.CompletedTask; 30 | } 31 | 32 | public EntityId GetEntityId(object instance) 33 | { 34 | return null; 35 | } 36 | 37 | public void MockSeveralContributorsExist(List testData) => 38 | _queryMock.Execute(Unit.Default).Returns(testData); 39 | } 40 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextA/SampleApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextA 5 | { 6 | public class SampleApplicationService : IEventHandler, IEventHandler, IEventHandler, IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | 10 | public SampleApplicationService(EventInvocationSpy eventSpy) 11 | { 12 | _eventSpy = eventSpy; 13 | } 14 | 15 | public Task Handle(EventA @event) 16 | { 17 | return RegisterEvent(@event); 18 | } 19 | 20 | public Task Handle(EventC @event) 21 | { 22 | return RegisterEvent(@event); 23 | } 24 | 25 | public Task Handle(EventH @event) 26 | { 27 | return RegisterEvent(@event); 28 | } 29 | 30 | public Task Handle(EventI @event) 31 | { 32 | return RegisterEvent(@event); 33 | } 34 | 35 | private Task RegisterEvent(IEvent @event) 36 | { 37 | _eventSpy.RegisterInvocation(@event); 38 | return Task.CompletedTask; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /tests/api-tests/karate-config.js: -------------------------------------------------------------------------------- 1 | function fn() { 2 | // don't waste time waiting for a connection or if servers don't respond within 5 seconds 3 | karate.configure('connectTimeout', 5000); 4 | karate.configure('readTimeout', 10000); 5 | 6 | var env = karate.env; // get java system property 'karate.env' 7 | karate.log('karate.env system property is set:', env); 8 | if (!env) { 9 | env = 'dev'; // a custom 'intelligent' default 10 | } 11 | var config = { // base config JSON 12 | vmAppUrl: 'http://localhost:5000', 13 | auth0: { 14 | domain: 'volley-mgmt-dev', 15 | clientId: 'A4tbWQojYmGgmxiFcA21KeoFbnG4c5Ex', 16 | audience: 'https://api.dev.volley-mgmt.org.ua', 17 | clientSecret: java.lang.System.getenv('VM_KARATE_AUTH0_CLIENT_SECRET') 18 | } 19 | }; 20 | 21 | if (env === 'staging') { 22 | config.vmAppUrl = 'https://staging-volley-mgmt.azurewebsites.net/'; 23 | config.auth0.domain = 'volley-mgmt-staging'; 24 | config.auth0.clientId = 'gHFwhskwh3HLP7fYobiUFSW3BK8055DL'; 25 | config.auth0.audience = 'https://api.staging.volley-mgmt.org.ua'; 26 | } 27 | 28 | // Authenticate request 29 | var result = karate.callSingle('file:./common/authenticate.feature', config); 30 | config.auth_header = 'Bearer ' + result.access_token; 31 | 32 | return config; 33 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Players.UnitTests/Players/CreatePlayer.feature: -------------------------------------------------------------------------------- 1 | Feature: Create Player 2 | In order to manage players in the tournament 3 | As a captain 4 | I want to create player 5 | 6 | @ab:1022 @unit @azurecloud 7 | Scenario: Create player 8 | Given I have CreateRequest 9 | | FirstName | LastName | 10 | | John | Smith | 11 | When I execute Create 12 | Then player is created 13 | And player is returned 14 | And PlayerCreated event is produced 15 | | TenantId | Version | PlayerId | FirstName | LastName | 16 | | | | player1 | John | Smith | 17 | And PlayerCreated event is Public 18 | 19 | @ab:1022 @unit 20 | Scenario: Validation cases 21 | Given I have CreateRequest 22 | | FirstName | LastName | 23 | | | | 24 | When I execute Create 25 | Then player is not created 26 | And ValidationError is returned 27 | And PlayerCreated event is not produced 28 | 29 | Examples: 30 | | FirstName | LastName | 31 | | <60+ symbols name> | Smith | 32 | | | Smith | 33 | | | Smith | 34 | | John | <60+ symbols name> | 35 | | John | | 36 | | John | | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/PlayersAzureStorageMigrationTask.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Serilog; 3 | using System.Composition; 4 | using System.Threading.Tasks; 5 | using VolleyM.Infrastructure.Players.AzureStorage.TableConfiguration; 6 | using VolleyM.Tools.MigrationTool.Contracts; 7 | 8 | namespace VolleyM.Infrastructure.Players.AzureStorage 9 | { 10 | [Export(typeof(IMigrationTask))] 11 | public class PlayersAzureStorageMigrationTask : IMigrationTask 12 | { 13 | private PlayersContextTableStorageOptions _options; 14 | private TableConfiguration.TableConfiguration _tableConfig; 15 | 16 | public Task Initialize(IConfiguration config) 17 | { 18 | _options = config.GetSection("PlayersContextTableStorageOptions") 19 | .Get(); 20 | 21 | _tableConfig = new TableConfiguration.TableConfiguration(_options); 22 | 23 | Log.Information("PlayersContext initialization complete."); 24 | return Task.CompletedTask; 25 | } 26 | 27 | public async Task MigrateUp() 28 | { 29 | await _tableConfig.ConfigureTables().ToEither(); 30 | Log.Information("PlayersContext migration complete."); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/Fixture/ContextB/SamplePublicApplicationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using VolleyM.Domain.Contracts.EventBroker; 3 | 4 | namespace VolleyM.Infrastructure.EventBroker.UnitTests.Fixture.ContextB 5 | { 6 | public class SamplePublicApplicationService : IEventHandler, IEventHandler, IEventHandler, IEventHandler 7 | { 8 | private readonly EventInvocationSpy _eventSpy; 9 | 10 | public SamplePublicApplicationService(EventInvocationSpy eventSpy) 11 | { 12 | _eventSpy = eventSpy; 13 | } 14 | 15 | public Task Handle(EventD @event) 16 | { 17 | return RegisterEvent(@event); 18 | } 19 | 20 | public Task Handle(EventF @event) 21 | { 22 | return RegisterEvent(@event); 23 | } 24 | 25 | public Task Handle(EventG @event) 26 | { 27 | return RegisterEvent(@event); 28 | } 29 | 30 | public Task Handle(EventH @event) 31 | { 32 | return RegisterEvent(@event); 33 | } 34 | 35 | private Task RegisterEvent(IEvent @event) 36 | { 37 | _eventSpy.RegisterInvocation(@event); 38 | return Task.CompletedTask; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /docs/adr/index.md: -------------------------------------------------------------------------------- 1 | # Architectural Decision Log 2 | 3 | This log lists the architectural decisions for [project name]. 4 | 5 | 6 | 7 | - [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records 8 | - [ADR-0001](0001-use-auth0-as-authentication-provider.md) - Use Auth0 as authentication provider 9 | - [ADR-0002](0002-use-sendgrid-as-mailing-system.md) - Use SendGrid for sending emails 10 | - [ADR-0003](0003-use-angular-for-spa.md) - Use Angular Framework for VolleyManagement.SPA container 11 | - [ADR-0004](0004-use-docker-for-hosting-application.md) - Use Docker for hosting application 12 | - [ADR-0005](0005-use-azure-app-services-for-hosting.md) - Use Azure App Services for deploying VolleyManagement.API container 13 | - [ADR-0006](0006-use-azure-table-storage-for-persistence.md) - Use Azure Table Storage for persistence 14 | - [ADR-0007](0007-follow-optimistic-concurrency.md) - Use Optimistic Concurrency 15 | 16 | 17 | 18 | For new ADRs, please use [template.md](template.md) as basis. 19 | More information on MADR is available at . 20 | General information about architectural decision records is available at . 21 | -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Hardcoded/HardcodedRolesStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using LanguageExt; 3 | using VolleyM.Domain.Contracts; 4 | using VolleyM.Domain.Framework.Authorization; 5 | using VolleyM.Domain.IdentityAndAccess.RolesAggregate; 6 | 7 | namespace VolleyM.Infrastructure.Hardcoded 8 | { 9 | public class HardcodedRolesStore : IRolesStore 10 | { 11 | private static readonly RoleId _visitor = new RoleId("visitor"); 12 | private static readonly RoleId _sysAdmin = new RoleId("sysadmin"); 13 | 14 | private readonly Dictionary _roles = new Dictionary(); 15 | 16 | public HardcodedRolesStore() 17 | { 18 | var visitor = new Role(_visitor); 19 | visitor.AddPermission(new Permission("contributors", "getall")); 20 | 21 | _roles[_visitor] = visitor; 22 | 23 | var sysadmin = new Role(_sysAdmin); 24 | sysadmin.AddPermission(new Permission("contributors", "getall")); 25 | sysadmin.AddPermission(new Permission("players", "create")); 26 | sysadmin.AddPermission(new Permission("players", "getall")); 27 | 28 | _roles[_sysAdmin] = sysadmin; 29 | } 30 | 31 | public EitherAsync Get(RoleId roleId) 32 | { 33 | if (_roles.TryGetValue(roleId, out var role)) 34 | { 35 | return role; 36 | } 37 | return Error.NotFound(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.IdentityAndAccess.UnitTests/Fixture/AzureCloudIdentityAndAccessOneTimeFixture.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.Configuration; 3 | using VolleyM.Domain.UnitTests.Framework; 4 | using VolleyM.Infrastructure.IdentityAndAccess.AzureStorage.TableConfiguration; 5 | 6 | namespace VolleyM.Domain.IdentityAndAccess.UnitTests.Fixture 7 | { 8 | public class AzureCloudIdentityAndAccessOneTimeFixture : IOneTimeTestFixture 9 | { 10 | private TableConfiguration _tableConfig; 11 | private IdentityContextTableStorageOptions _options; 12 | 13 | public void OneTimeSetup(IConfiguration configuration) 14 | { 15 | _options = configuration.GetSection("IdentityContextTableStorageOptions") 16 | .Get(); 17 | 18 | _tableConfig = new TableConfiguration(_options); 19 | var result = _tableConfig.ConfigureTables().ToEither().Result; 20 | 21 | result.IsRight.Should().BeTrue("Azure Storage should be configured correctly"); 22 | } 23 | 24 | public void OneTimeTearDown() 25 | { 26 | var result = _tableConfig.CleanTables().ToEither().Result; 27 | result.IsRight.Should().BeTrue("Azure Storage should be cleaned up correctly"); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.IdentityAndAccess.AzureStorage/IdentityAndAccessAzureStorageMigrationTask.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Serilog; 3 | using System.Composition; 4 | using System.Threading.Tasks; 5 | using VolleyM.Infrastructure.IdentityAndAccess.AzureStorage.TableConfiguration; 6 | using VolleyM.Tools.MigrationTool.Contracts; 7 | 8 | namespace VolleyM.Infrastructure.IdentityAndAccess.AzureStorage 9 | { 10 | [Export(typeof(IMigrationTask))] 11 | public class IdentityAndAccessAzureStorageMigrationTask : IMigrationTask 12 | { 13 | private IdentityContextTableStorageOptions _options; 14 | private TableConfiguration.TableConfiguration _tableConfig; 15 | 16 | public Task Initialize(IConfiguration config) 17 | { 18 | _options = config.GetSection("IdentityContextTableStorageOptions") 19 | .Get(); 20 | 21 | _tableConfig = new TableConfiguration.TableConfiguration(_options); 22 | 23 | Log.Information("IaAContext initialization complete."); 24 | return Task.CompletedTask; 25 | } 26 | 27 | public async Task MigrateUp() 28 | { 29 | await _tableConfig.ConfigureTables().ToEither(); 30 | Log.Information("IaAContext migration complete."); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker.MassTransit/MassTransitEventPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MassTransit; 3 | using MassTransit.Context; 4 | using Microsoft.Extensions.Logging; 5 | using Serilog; 6 | using SimpleInjector; 7 | using VolleyM.Domain.Framework.EventBus; 8 | 9 | namespace VolleyM.Infrastructure.EventBroker.MassTransit 10 | { 11 | public class MassTransitEventPublisher : IEventPublisher 12 | { 13 | private IBusControl _bus; 14 | private BusHandle _busHandle; 15 | 16 | public MassTransitEventPublisher(Container container, ILoggerFactory loggerFactory) 17 | { 18 | LogContext.ConfigureCurrentLogContext(loggerFactory); 19 | 20 | _bus = Bus.Factory.CreateUsingInMemory(cfg => 21 | { 22 | cfg.UseSerilog(Log.Logger); 23 | cfg.ReceiveEndpoint(ep => 24 | { 25 | ep.ConfigureConsumers(container); 26 | }); 27 | }); 28 | } 29 | 30 | public async Task StartBus() 31 | { 32 | _busHandle = await _bus.StartAsync(); 33 | 34 | await _busHandle.Ready; 35 | } 36 | 37 | public Task PublishEvent(TEvent @event) where TEvent : class 38 | { 39 | return _bus.Publish(@event); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/adr/0000-use-markdown-architectural-decision-records.md: -------------------------------------------------------------------------------- 1 | # Use Markdown Architectural Decision Records 2 | 3 | ## Context and Problem Statement 4 | 5 | We want to record architectural decisions made in this project. 6 | Which format and structure should these records follow? 7 | 8 | ## Considered Options 9 | 10 | * [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records 11 | * [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" 12 | * [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements 13 | * Other templates listed at 14 | * Formless – No conventions for file format and structure 15 | 16 | ## Decision Outcome 17 | 18 | Chosen option: "MADR 2.1.2", because 19 | 20 | * Implicit assumptions should be made explicit. 21 | Design documentation is important to enable people understanding the decisions later on. 22 | See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). 23 | * The MADR format is lean and fits our development style. 24 | * The MADR structure is comprehensible and facilitates usage & maintenance. 25 | * The MADR project is vivid. 26 | * Version 2.1.2 is the latest one available when starting to document ADRs. 27 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Infrastructure.EventBroker.UnitTests/EvenBrokerTestSetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using BoDi; 4 | using TechTalk.SpecFlow; 5 | using VolleyM.Domain.UnitTests.Framework; 6 | using VolleyM.Infrastructure.Bootstrap; 7 | 8 | namespace VolleyM.Infrastructure.EventBroker.UnitTests 9 | { 10 | [Binding] 11 | public class EvenBrokerTestSetup : DomainTestSetupBase 12 | { 13 | public EvenBrokerTestSetup(IObjectContainer objectContainer, FeatureContext featureContext) 14 | : base(objectContainer, featureContext) { } 15 | 16 | [BeforeTestRun] 17 | public static void OneTimeSetup() 18 | { 19 | TestRunFixtureBase.BeforeTestRun(); 20 | } 21 | 22 | [AfterTestRun] 23 | public static void OneTimeTearDown() 24 | { 25 | TestRunFixtureBase.AfterTestRun(); 26 | } 27 | 28 | protected override ITestFixture CreateTestFixture(TestTarget target) 29 | { 30 | return target switch 31 | { 32 | TestTarget.Unit => new UnitEventBrokerTestFixture(), 33 | _ => throw new NotSupportedException() 34 | }; 35 | } 36 | 37 | protected override bool RequiresAuthorizationFixture => false; 38 | 39 | protected override Type GetConcreteTestFixtureType => typeof(IEventBrokerTestFixture); 40 | 41 | protected override IEnumerable GetAssemblyBootstrappers(TestTarget target) 42 | { 43 | return new[] { new EventBrokerAssemblyBootstrapper() }; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.UnitTests.Framework/VolleyM.Domain.UnitTests.Framework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/adr/0006-use-azure-table-storage-for-persistence.md: -------------------------------------------------------------------------------- 1 | # Use Azure Table Storage for persistence 2 | 3 | * Status: ✔ accepted 4 | * Deciders: Sergii Diachenko 5 | * Date: 2019-08-23 6 | 7 | ## Context and Problem Statement 8 | 9 | We need a persistent state for the system. Previously I've used Azure SQL but it is quite pricey but has it's own set of features including relational model. But given that system is not very complex I can model persistence mechanism for NoSQL model. 10 | 11 | ## Decision Drivers 12 | 13 | * Cost 14 | * Backup capabilities 15 | 16 | ## Considered Options 17 | 18 | * Azure SQL 19 | * Azure Table Storage 20 | 21 | ## Decision Outcome 22 | 23 | Chosen option: "Azure Table Storage", because cost. 24 | 25 | ### Positive Consequences 26 | 27 | * Persistence bill should be down from ~$5/month to less than $1/month 28 | 29 | ### Negative Consequences 30 | 31 | * Students in IT Academy won't be exposed to a relational model, which at this moment dominates work they will have to be doing. We will have to come up with a strategy to get them good experience. 32 | 33 | ## Pros and Cons of the Options 34 | 35 | ### Azure SQL 36 | 37 | * ✔ Has good backup capabilities 38 | * ✔ Relational model 39 | * ❌ Cost 40 | * ✔ Local dev env support 41 | 42 | ### Azure Table Storage 43 | 44 | * ✔ Storage container can be backed up 45 | * ❌ Relational model 46 | * ✔ Cost 47 | * ✔ Local dev env support 48 | -------------------------------------------------------------------------------- /src/Domain/VolleyM.Domain.Contracts/Error/Error.cs: -------------------------------------------------------------------------------- 1 | namespace VolleyM.Domain.Contracts 2 | { 3 | public class Error 4 | { 5 | protected Error(ErrorType type, string message) 6 | { 7 | Type = type; 8 | Message = message; 9 | } 10 | 11 | public ErrorType Type { get; } 12 | 13 | public string Message { get; } 14 | 15 | public static Error Conflict(string message = "Entity already exists") 16 | => new Error(ErrorType.Conflict, message); 17 | public static Error NotFound(string message = "Entity not found") 18 | => new Error(ErrorType.NotFound, message); 19 | public static Error InternalError(string message) 20 | => new Error(ErrorType.InternalError, message); 21 | public static Error NotAuthorized(string message) 22 | => new Error(ErrorType.NotAuthorized, message); 23 | public static Error NotAuthenticated() 24 | => new Error(ErrorType.NotAuthenticated, "User is not authenticated"); 25 | public static Error FeatureDisabled() 26 | => new Error(ErrorType.FeatureDisabled, "Feature disabled"); 27 | public static Error DesignViolation(string message) 28 | => new Error(ErrorType.DesignViolation, message); 29 | public static Error ConcurrencyCheckFailed(string message = "Entity is outdated. Please retry operation.") 30 | => new Error(ErrorType.ConcurrencyCheckFailed, message); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.EventBroker/EventBrokerAssemblyBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Composition; 2 | using AutoMapper; 3 | using SimpleInjector; 4 | using VolleyM.Domain.Contracts.EventBroker; 5 | using VolleyM.Domain.Framework.EventBroker; 6 | using VolleyM.Infrastructure.Bootstrap; 7 | using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; 8 | 9 | namespace VolleyM.Infrastructure.EventBroker 10 | { 11 | [Export(typeof(IAssemblyBootstrapper))] 12 | public class EventBrokerAssemblyBootstrapper : IAssemblyBootstrapper 13 | { 14 | public void RegisterDependencies(Container container, IConfiguration config) 15 | { 16 | container.Register(Lifestyle.Singleton); 17 | container.RegisterInitializer( p => p.Initialize()); 18 | 19 | container.Register(Lifestyle.Singleton); 20 | 21 | container.RegisterDecorator( 22 | typeof(IEventHandler<>), 23 | typeof(AsyncScopedEventHandlerProxy<>), 24 | Lifestyle.Singleton); 25 | } 26 | 27 | public bool HasDomainComponents { get; } = false; 28 | 29 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 30 | 31 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 32 | { 33 | // do nothing 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Architecture.UnitTests/TestExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using FluentAssertions; 6 | using NetArchTest.Rules; 7 | using NetArchTest.Rules.Policies; 8 | 9 | namespace VolleyM.Architecture.UnitTests 10 | { 11 | public static class TestExtensions 12 | { 13 | public static PredicateList ThatNotCompilerGenerated(this Types types) 14 | { 15 | return types.That().DoNotHaveCustomAttribute(typeof(CompilerGeneratedAttribute)); 16 | } 17 | 18 | public static void AssertHasNoViolations(this PolicyResults result) 19 | { 20 | var lines = new List { 21 | $"{result.Description}", 22 | $"{result.Name} policy has been violated by following types:" 23 | }; 24 | 25 | 26 | static string GetFirstViolators(IEnumerable failingTypes) 27 | { 28 | return string.Join(',', failingTypes.Take(3).Select(t => t.FullName).Union(new[] { "..." })); 29 | } 30 | 31 | lines.AddRange( 32 | result.Results 33 | .Where(r => !r.IsSuccessful) 34 | .Select(policyResult => policyResult.Name + ": " + GetFirstViolators(policyResult.FailingTypes))); 35 | 36 | result.HasViolations.Should().BeFalse(string.Join(Environment.NewLine, lines)); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/InfrastructurePlayersAzureStorageBootstrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Composition; 2 | using System.Reflection; 3 | using AutoMapper; 4 | using Microsoft.Extensions.Configuration; 5 | using SimpleInjector; 6 | using VolleyM.Domain.Contracts; 7 | using VolleyM.Domain.Players.PlayerAggregate; 8 | using VolleyM.Infrastructure.Bootstrap; 9 | using VolleyM.Infrastructure.Players.AzureStorage.TableConfiguration; 10 | using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; 11 | 12 | namespace VolleyM.Infrastructure.Players.AzureStorage 13 | { 14 | [Export(typeof(IAssemblyBootstrapper))] 15 | public class InfrastructurePlayersAzureStorageBootstrapper : IAssemblyBootstrapper 16 | { 17 | public void RegisterDependencies(Container container, IConfiguration config) 18 | { 19 | container.Register(Lifestyle.Scoped); 20 | 21 | var options = config.GetSection("PlayersContextTableStorageOptions") 22 | .Get(); 23 | 24 | container.RegisterInstance(options); 25 | 26 | container.Register(typeof(IQuery<,>), Assembly.GetAssembly(GetType()), Lifestyle.Scoped); 27 | } 28 | 29 | public bool HasDomainComponents { get; } = false; 30 | 31 | public IDomainComponentDependencyRegistrar DomainComponentDependencyRegistrar { get; } = null; 32 | 33 | public void RegisterMappingProfiles(MapperConfigurationExpression mce) 34 | => mce.AddProfile(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Client/VolleyM.API.Players/PlayersController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using AutoMapper; 4 | using Microsoft.AspNetCore.Mvc; 5 | using VolleyM.API.Contracts; 6 | using VolleyM.Domain.Contracts; 7 | using VolleyM.Domain.Players; 8 | using VolleyM.Domain.Players.Handlers; 9 | 10 | using ApiPlayer = VolleyM.API.Players.Player; 11 | using DomainPlayer = VolleyM.Domain.Players.PlayerAggregate.Player; 12 | 13 | namespace VolleyM.API.Players 14 | { 15 | [Route("api/[controller]")] 16 | [ApiController] 17 | public class PlayersController : ControllerBase 18 | { 19 | private readonly IRequestHandler _createHandler; 20 | private readonly IRequestHandler> _getAllHandler; 21 | private readonly IMapper _mapper; 22 | 23 | public PlayersController( 24 | IRequestHandler createHandler, 25 | IRequestHandler> getAllHandler, 26 | IMapper mapper) 27 | { 28 | _createHandler = createHandler; 29 | _mapper = mapper; 30 | _getAllHandler = getAllHandler; 31 | } 32 | 33 | [HttpPost] 34 | [Route("")] 35 | public Task Create(Create.Request request) 36 | { 37 | return _createHandler.ExecuteHandler(request, _mapper.Map); 38 | } 39 | 40 | [HttpGet] 41 | [Route("")] 42 | public Task GetAll() 43 | { 44 | return _getAllHandler.ExecuteHandler(new GetAll.Request(), _mapper.Map>); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/Infrastructure/VolleyM.Infrastructure.Players.AzureStorage/PlayersAzureStorageMappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using VolleyM.Domain.Contracts; 3 | using VolleyM.Domain.Players; 4 | using VolleyM.Domain.Players.PlayerAggregate; 5 | 6 | namespace VolleyM.Infrastructure.Players.AzureStorage 7 | { 8 | public class PlayersAzureStorageMappingProfile : Profile 9 | { 10 | public PlayersAzureStorageMappingProfile() 11 | { 12 | EntityToFactoryMap(); 13 | EntityToDtoMap(); 14 | } 15 | 16 | private void EntityToFactoryMap() 17 | { 18 | CreateMap() 19 | .ForMember(m => m.Id, 20 | m => m.MapFrom(src => new PlayerId(src.RowKey))) 21 | .ForMember(m => m.Tenant, 22 | m => m.MapFrom(src => new TenantId(src.PartitionKey))) 23 | .ForMember(m => m.Version, 24 | m => m.MapFrom(pe => new Version(pe.ETag))) 25 | .ForMember(m => m.FirstName, 26 | m => m.MapFrom(pe => pe.FirstName)) 27 | .ForMember(m => m.LastName, 28 | m => m.MapFrom(pe => pe.LastName)); 29 | } 30 | private void EntityToDtoMap() 31 | { 32 | CreateMap() 33 | .ForMember(m => m.Id, 34 | m => m.MapFrom(src => new PlayerId(src.RowKey))) 35 | .ForMember(m => m.Tenant, 36 | m => m.MapFrom(src => new TenantId(src.PartitionKey))) 37 | .ForMember(m => m.Version, 38 | m => m.MapFrom(pe => new Version(pe.ETag))) 39 | .ForMember(m => m.FirstName, 40 | m => m.MapFrom(pe => pe.FirstName)) 41 | .ForMember(m => m.LastName, 42 | m => m.MapFrom(pe => pe.LastName)); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/Authorization/CheckAccess.feature: -------------------------------------------------------------------------------- 1 | Feature: Check Access 2 | Validates user permission against given operation 3 | 4 | @ab:1026 5 | Scenario: User is authorized when user role has permission 6 | Given user has RoleA role assigned 7 | And RoleA has Permission1 8 | When I check access to Permission1 9 | Then access is granted 10 | 11 | @ab:1026 12 | Scenario: User is not authorized when user role has no permission 13 | Given user has RoleA role assigned 14 | And RoleA has Permission1 15 | When I check access to Permission2 16 | Then access is denied 17 | 18 | @ab:1026 19 | Scenario: User is not authorized when exception occured 20 | Given role storage returns error 21 | When I check access to Permission1 22 | Then access is denied 23 | 24 | @ab:1026 25 | Scenario Outline: AuthZ Handler role authorization 26 | Given user has authz.handler role assigned 27 | When I check access to permission from '' for '' 28 | Then access is granted 29 | 30 | Examples: predefined AuthZ handler permissions 31 | | permissionContext | permissionAction | 32 | | IdentityAndAccess | GetUser | 33 | | IdentityAndAccess | CreateUser | 34 | 35 | @ab:1026 36 | Scenario: Permission check is case insensitive 37 | Given user has RoleB role assigned 38 | And RoleB has LowerCasePermission3 39 | When I check access to UpperCasePermission3 40 | Then access is granted 41 | 42 | @ab:1026 43 | Scenario: User does not have any role assigned 44 | Given user has no role 45 | When I check access to Permission1 46 | Then access is denied -------------------------------------------------------------------------------- /tools/VolleyM.Tools.MigrationTool/VolleyM.Tools.MigrationTool.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/PRIVACY-POLICY-DEV.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy # 2 | 3 | Your privacy is important to us. It is Volley Management's policy to respect your privacy regarding any information we may collect from you across our website, http://dev.volley-mgmt.org.ua, and other sites we own and operate. 4 | 5 | We only ask for personal information when we truly need it to provide a service to you. We collect it by fair and lawful means, with your knowledge and consent. We also let you know why we’re collecting it and how it will be used. 6 | 7 | We only retain collected information for as long as necessary to provide you with your requested service. What data we store, we’ll protect within commercially acceptable means to prevent loss and theft, as well as unauthorised access, disclosure, copying, use or modification. 8 | 9 | We don’t share any personally identifying information publicly or with third-parties, except when required to by law. 10 | 11 | Our website may link to external sites that are not operated by us. Please be aware that we have no control over the content and practices of these sites, and cannot accept responsibility or liability for their respective privacy policies. 12 | 13 | You are free to refuse our request for your personal information, with the understanding that we may be unable to provide you with some of your desired services. 14 | 15 | Your continued use of our website will be regarded as acceptance of our practices around privacy and personal information. If you have any questions about how we handle user data and personal information, feel free to contact us. 16 | 17 | This policy is effective as of 3 September 2019. 18 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Architecture.UnitTests/PackageStructureConditionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using FluentAssertions; 5 | using NetArchTest.Rules; 6 | 7 | namespace VolleyM.Architecture.UnitTests 8 | { 9 | public static class PackageStructureConditionExtensions 10 | { 11 | public static ConditionList ResideInAllowedNamespace(this Conditions conditions, string parentNs, string[] allowedNames) 12 | { 13 | var result = conditions.ResideInNamespace($"{parentNs}.{allowedNames[0]}"); 14 | 15 | for (var i = 1; i < allowedNames.Length; i++) 16 | { 17 | result = result.Or().ResideInNamespace($"{parentNs}.{allowedNames[i]}"); 18 | } 19 | 20 | return result; 21 | } 22 | 23 | public static void AssertContextNameIsAllowed(this Assembly assembly, IEnumerable allowedNames) 24 | { 25 | AssertNameIsAllowed(assembly, 2, allowedNames); 26 | } 27 | 28 | public static void AssertNameIsAllowed(this Assembly assembly, int level, IEnumerable allowedNames) 29 | { 30 | var contextNames = assembly.GetName().Name.Split('.', StringSplitOptions.RemoveEmptyEntries); 31 | 32 | if (contextNames.Length < 3) 33 | { 34 | //this is covered by another case 35 | } 36 | else 37 | { 38 | contextNames[level].Should().BeOneOf(allowedNames, "Packages should use allowed names"); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Framework.UnitTests/EventBroker/EventProducerDecorator.feature: -------------------------------------------------------------------------------- 1 | Feature: Domain Event dispatching 2 | Wires events produced by the request handler into event bus 3 | 4 | @ab:1099 5 | Scenario: Handler produced event 6 | Given I have a handler which can produce events 7 | And handler produces event 8 | When I call decorated handler 9 | Then event is published to event broker 10 | 11 | @ab:1099 12 | Scenario: Handler produced no events 13 | Given I have a handler which can produce events 14 | And handler does not produce event 15 | When I call decorated handler 16 | Then nothing is published to event broker 17 | 18 | @ab:1099 19 | Scenario: Handler produced several events 20 | Given I have a handler which can produce events 21 | And handler produced several events 22 | When I call decorated handler 23 | Then all events are published to event broker 24 | 25 | @ab:1099 26 | Scenario: Handler returns value 27 | Given I have a handler which can produce events 28 | When I call decorated handler 29 | Then handler result should be returned 30 | 31 | @ab:1099 32 | Scenario: Handler does not produce events 33 | Given I have a handler which does not produce events 34 | When I call decorated handler 35 | Then handler result should be returned 36 | 37 | @ab:1099 38 | Scenario: Handler does not have domain events property initialized 39 | Given I have a handler which does not have domain events initialized 40 | When I call decorated handler 41 | Then DesignViolation error should be returned with message 'DomainEvents property should be initialized when you inherit ICanProduceEvents interface' 42 | -------------------------------------------------------------------------------- /tests/unit-tests/VolleyM.Domain.Contributors.UnitTests/ContributorsTestSetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using BoDi; 4 | using TechTalk.SpecFlow; 5 | using VolleyM.Domain.UnitTests.Framework; 6 | using VolleyM.Infrastructure.Bootstrap; 7 | 8 | namespace VolleyM.Domain.Contributors.UnitTests 9 | { 10 | [Binding] 11 | public class ContributorsTestSetup : DomainTestSetupBase 12 | { 13 | public ContributorsTestSetup(IObjectContainer objectContainer, FeatureContext featureContext) 14 | : base(objectContainer, featureContext) 15 | { 16 | } 17 | 18 | [BeforeTestRun] 19 | public static void BeforeTestRun() 20 | { 21 | TestRunFixtureBase.BeforeTestRun(); 22 | } 23 | 24 | [AfterTestRun] 25 | public static void AfterTestRun() 26 | { 27 | TestRunFixtureBase.AfterTestRun(); 28 | } 29 | protected override ITestFixture CreateTestFixture(TestTarget target) 30 | { 31 | return target switch 32 | { 33 | TestTarget.Unit => new UnitContributorsTestFixture(), 34 | _ => throw new NotSupportedException() 35 | }; 36 | } 37 | 38 | protected override bool RequiresAuthorizationFixture => true; 39 | 40 | protected override Type GetConcreteTestFixtureType => typeof(IContributorsTestFixture); 41 | 42 | protected override IEnumerable GetAssemblyBootstrappers(TestTarget target) => 43 | new[] { 44 | new DomainContributorsAssemblyBootstrapper() 45 | }; 46 | } 47 | } --------------------------------------------------------------------------------