diff --git a/build.cake b/build.cake index d6166d3..d61edc6 100644 --- a/build.cake +++ b/build.cake @@ -28,7 +28,7 @@ var netFrameworkUnitTestAssemblies = new [] @"./src/Evelyn.Storage.EventStore.Tests/bin/"+compileConfig+"/net461/Evelyn.Storage.EventStore.Tests.dll", }; var openCoverSettings = new OpenCoverSettings(); -var minCodeCoverage = 93d; +var minCodeCoverage = 92d; var coverallsRepoToken = "coveralls-repo-token-evelyn"; // integration testing diff --git a/src/Evelyn.Core.Tests/ReadModel/AccountProjects/AccountProjectsDtoSpecs.cs b/src/Evelyn.Core.Tests/ReadModel/AccountProjects/AccountProjectsDtoSpecs.cs new file mode 100644 index 0000000..8ce2bee --- /dev/null +++ b/src/Evelyn.Core.Tests/ReadModel/AccountProjects/AccountProjectsDtoSpecs.cs @@ -0,0 +1,17 @@ +namespace Evelyn.Core.Tests.ReadModel.AccountProjects +{ + using AutoFixture; + using Core.ReadModel.AccountProjects; + using Xunit; + + public class AccountProjectsDtoSpecs : DtoSpecs + { + [Fact] + public void Serialization() + { + var accountProjects = DataFixture.Create(); + + AssertSerializationOf(accountProjects); + } + } +} diff --git a/src/Evelyn.Core.Tests/ReadModel/ProjectList/ProjectListHandlerSpecs.cs b/src/Evelyn.Core.Tests/ReadModel/AccountProjects/AccountProjectsHandlerSpecs.cs similarity index 59% rename from src/Evelyn.Core.Tests/ReadModel/ProjectList/ProjectListHandlerSpecs.cs rename to src/Evelyn.Core.Tests/ReadModel/AccountProjects/AccountProjectsHandlerSpecs.cs index f2194cf..eb2eb60 100644 --- a/src/Evelyn.Core.Tests/ReadModel/ProjectList/ProjectListHandlerSpecs.cs +++ b/src/Evelyn.Core.Tests/ReadModel/AccountProjects/AccountProjectsHandlerSpecs.cs @@ -1,31 +1,33 @@ -namespace Evelyn.Core.Tests.ReadModel.ProjectList +namespace Evelyn.Core.Tests.ReadModel.AccountProjects { using System.Collections.Generic; - using System.Linq; using System.Threading.Tasks; using AutoFixture; + using Core.ReadModel.AccountProjects; + using Core.ReadModel.Events; using Core.ReadModel.ProjectList; using CQRSlite.Events; using CQRSlite.Routing; - using Evelyn.Core.ReadModel.Events; using FluentAssertions; using TestStack.BDDfy; using Xunit; - public class ProjectListHandlerSpecs : HandlerSpecs + public class AccountProjectsHandlerSpecs : HandlerSpecs { - private List _eventsProject1; - private List _eventsProject2; + private readonly List _eventsProject1; + private readonly List _eventsProject2; + private readonly string _accountId; private ProjectCreated _event1; private ProjectCreated _event2; - private List _retrievedProjectList; + private AccountProjectsDto _retrievedAccountProjects; - public ProjectListHandlerSpecs() + public AccountProjectsHandlerSpecs() { _eventsProject1 = new List(); _eventsProject2 = new List(); + _accountId = DataFixture.Create(); } [Fact] @@ -49,14 +51,16 @@ public void MultipleProjectsCreated() protected override void RegisterHandlers(Router router) { - var handler = new ProjectListHandler(ProjectsStore); + var handler = new AccountProjectsHandler(AccountProjectsStore); router.RegisterHandler(handler.Handle); } private void GivenAnProjectIsCreated() { - _event1 = DataFixture.Create(); - _event1.Version = _eventsProject1.Count + 1; + _event1 = DataFixture.Build() + .With(pc => pc.AccountId, _accountId) + .With(pc => pc.Version, _eventsProject1.Count + 1) + .Create(); _eventsProject1.Add(_event1); GivenWePublish(_event1); @@ -64,8 +68,10 @@ private void GivenAnProjectIsCreated() private void GivenAnotherProjectIsCreated() { - _event2 = DataFixture.Create(); - _event2.Version = _eventsProject2.Count + 1; + _event2 = DataFixture.Build() + .With(pc => pc.AccountId, _accountId) + .With(pc => pc.Version, _eventsProject2.Count + 1) + .Create(); _eventsProject2.Add(_event2); GivenWePublish(_event2); @@ -73,18 +79,18 @@ private void GivenAnotherProjectIsCreated() private async Task WhenWeGetTheProjectList() { - _retrievedProjectList = (await ReadModelFacade.GetProjects()).ToList(); + _retrievedAccountProjects = await ReadModelFacade.GetProjects(_accountId); } private void ThenTheProjectIsAddedToTheProjectList() { - _retrievedProjectList.Count.Should().Be(1); + _retrievedAccountProjects.Projects.Count.Should().Be(1); ThenThereIsAnProjectInTheListFor(_event1); } private void ThenBothProjectsAreInTheProjectList() { - _retrievedProjectList.Count().Should().Be(2); + _retrievedAccountProjects.Projects.Count.Should().Be(2); ThenThereIsAnProjectInTheListFor(_event1); ThenThereIsAnProjectInTheListFor(_event2); @@ -92,9 +98,9 @@ private void ThenBothProjectsAreInTheProjectList() private void ThenThereIsAnProjectInTheListFor(ProjectCreated ev) { - ProjectsStore.Get().GetAwaiter().GetResult().Should().Contain(project => - project.Id == ev.Id && - project.Name == ev.Name); + AccountProjectsStore.Get(_accountId) + .GetAwaiter().GetResult() + .Projects[ev.Id].Name.Should().Be(ev.Name); } } } diff --git a/src/Evelyn.Core.Tests/ReadModel/HandlerSpecs.cs b/src/Evelyn.Core.Tests/ReadModel/HandlerSpecs.cs index a657025..c51fbc0 100644 --- a/src/Evelyn.Core.Tests/ReadModel/HandlerSpecs.cs +++ b/src/Evelyn.Core.Tests/ReadModel/HandlerSpecs.cs @@ -2,6 +2,7 @@ { using System; using AutoFixture; + using Core.ReadModel.AccountProjects; using Core.ReadModel.ProjectDetails; using Core.ReadModel.ProjectList; using Core.ReadModel.ToggleDetails; @@ -19,13 +20,13 @@ protected HandlerSpecs() { DataFixture = new Fixture(); - ProjectsStore = new InMemoryDatabase(); - ProjectDetailsStore = new InMemoryDatabase(); - EnvironmentDetailsStore = new InMemoryDatabase(); - ToggleDetailsStore = new InMemoryDatabase(); + AccountProjectsStore = new InMemoryDatabase(); + ProjectDetailsStore = new InMemoryDatabase(); + EnvironmentDetailsStore = new InMemoryDatabase(); + ToggleDetailsStore = new InMemoryDatabase(); ReadModelFacade = new DatabaseReadModelFacade( - ProjectsStore, + AccountProjectsStore, ProjectDetailsStore, EnvironmentDetailsStore, ToggleDetailsStore); @@ -39,13 +40,13 @@ protected HandlerSpecs() protected IReadModelFacade ReadModelFacade { get; } - protected IDatabase ProjectsStore { get; } + protected IDatabase AccountProjectsStore { get; } - protected IDatabase ProjectDetailsStore { get; } + protected IDatabase ProjectDetailsStore { get; } - protected IDatabase EnvironmentDetailsStore { get; set; } + protected IDatabase EnvironmentDetailsStore { get; set; } - protected IDatabase ToggleDetailsStore { get; set; } + protected IDatabase ToggleDetailsStore { get; set; } protected Exception ThrownException { get; set; } diff --git a/src/Evelyn.Core.Tests/ReadModel/ProjectList/ProjectListDtoSpecs.cs b/src/Evelyn.Core.Tests/ReadModel/ProjectList/ProjectListDtoSpecs.cs deleted file mode 100644 index b05c7a7..0000000 --- a/src/Evelyn.Core.Tests/ReadModel/ProjectList/ProjectListDtoSpecs.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Evelyn.Core.Tests.ReadModel.ProjectList -{ - using AutoFixture; - using Core.ReadModel.ProjectList; - using Xunit; - - public class ProjectListDtoSpecs : DtoSpecs - { - [Fact] - public void Serialization() - { - var projectList = DataFixture.Create(); - - AssertSerializationOf(projectList); - } - } -} diff --git a/src/Evelyn.Core/DependencyInjection/ConfigureInProcessHandlerOptions.cs b/src/Evelyn.Core/DependencyInjection/ConfigureInProcessHandlerOptions.cs index 28bcefd..5539a45 100644 --- a/src/Evelyn.Core/DependencyInjection/ConfigureInProcessHandlerOptions.cs +++ b/src/Evelyn.Core/DependencyInjection/ConfigureInProcessHandlerOptions.cs @@ -16,7 +16,7 @@ public void Configure(HandlerOptions options) { typeof(ProjectCommandHandler), typeof(ProjectDetailsHandler), - typeof(ProjectListHandler), + typeof(AccountProjectsHandler), typeof(EnvironmentDetailsHandler), typeof(ToggleDetailsHandler) }); diff --git a/src/Evelyn.Core/DependencyInjection/InMemoryReadCache.cs b/src/Evelyn.Core/DependencyInjection/InMemoryReadCache.cs index c1e2ebc..c6d5a83 100644 --- a/src/Evelyn.Core/DependencyInjection/InMemoryReadCache.cs +++ b/src/Evelyn.Core/DependencyInjection/InMemoryReadCache.cs @@ -14,7 +14,7 @@ public static class InMemoryReadCache public static void InMemoryCache(this ReadModelCacheOptions parentOptions) #pragma warning restore SA1614 // Element parameter documentation must have text { - parentOptions.Services.TryAddSingleton(typeof(IDatabase<>), typeof(InMemoryDatabase<>)); + parentOptions.Services.TryAddSingleton(typeof(IDatabase<,>), typeof(InMemoryDatabase<,>)); } } } diff --git a/src/Evelyn.Core/DependencyInjection/InProcessHandlers.cs b/src/Evelyn.Core/DependencyInjection/InProcessHandlers.cs index a2e67bb..f4585fb 100644 --- a/src/Evelyn.Core/DependencyInjection/InProcessHandlers.cs +++ b/src/Evelyn.Core/DependencyInjection/InProcessHandlers.cs @@ -22,7 +22,7 @@ public static void SynchronouslyInProcess(this EventPublisherOptions parentOptio #pragma warning restore SA1614 // Element parameter documentation must have text { parentOptions.Services.TryAddSingleton(); - parentOptions.Services.TryAddSingleton(); + parentOptions.Services.TryAddSingleton(); parentOptions.Services.TryAddSingleton(); parentOptions.Services.TryAddSingleton(); diff --git a/src/Evelyn.Core/ReadModel/AccountProjects/AccountProjectsDto.cs b/src/Evelyn.Core/ReadModel/AccountProjects/AccountProjectsDto.cs new file mode 100644 index 0000000..80a24a3 --- /dev/null +++ b/src/Evelyn.Core/ReadModel/AccountProjects/AccountProjectsDto.cs @@ -0,0 +1,19 @@ +namespace Evelyn.Core.ReadModel.AccountProjects +{ + using System; + using System.Collections.Generic; + using ProjectList; + + public class AccountProjectsDto + { + public AccountProjectsDto(string accountId) + { + AccountId = accountId; + Projects = new Dictionary(); + } + + public string AccountId { get; private set; } + + public Dictionary Projects { get; private set; } + } +} diff --git a/src/Evelyn.Core/ReadModel/AccountProjects/AccountProjectsHandler.cs b/src/Evelyn.Core/ReadModel/AccountProjects/AccountProjectsHandler.cs new file mode 100644 index 0000000..1fad802 --- /dev/null +++ b/src/Evelyn.Core/ReadModel/AccountProjects/AccountProjectsHandler.cs @@ -0,0 +1,36 @@ +namespace Evelyn.Core.ReadModel.ProjectList +{ + using System.Threading; + using System.Threading.Tasks; + using CQRSlite.Events; + using Evelyn.Core.ReadModel.AccountProjects; + using Events; + using Infrastructure; + + public class AccountProjectsHandler + : ICancellableEventHandler + { + private readonly IDatabase _db; + + public AccountProjectsHandler(IDatabase db) + { + _db = db; + } + + public async Task Handle(ProjectCreated message, CancellationToken token) + { + AccountProjectsDto accountProjects; + try + { + accountProjects = await _db.Get(message.AccountId); + } + catch (NotFoundException) + { + accountProjects = new AccountProjectsDto(message.AccountId); + } + + accountProjects.Projects.Add(message.Id, new ProjectListDto(message.Id, message.Name)); + await _db.AddOrUpdate(accountProjects.AccountId, accountProjects); + } + } +} diff --git a/src/Evelyn.Core/ReadModel/ProjectList/ProjectListDto.cs b/src/Evelyn.Core/ReadModel/AccountProjects/ProjectListDto.cs similarity index 71% rename from src/Evelyn.Core/ReadModel/ProjectList/ProjectListDto.cs rename to src/Evelyn.Core/ReadModel/AccountProjects/ProjectListDto.cs index 7ca69dd..1f748ba 100644 --- a/src/Evelyn.Core/ReadModel/ProjectList/ProjectListDto.cs +++ b/src/Evelyn.Core/ReadModel/AccountProjects/ProjectListDto.cs @@ -10,8 +10,8 @@ public ProjectListDto(Guid id, string name) Name = name; } - public Guid Id { get; } + public Guid Id { get; private set; } - public string Name { get; } + public string Name { get; private set; } } } diff --git a/src/Evelyn.Core/ReadModel/DatabaseReadModelFacade.cs b/src/Evelyn.Core/ReadModel/DatabaseReadModelFacade.cs index 983ad63..114480f 100644 --- a/src/Evelyn.Core/ReadModel/DatabaseReadModelFacade.cs +++ b/src/Evelyn.Core/ReadModel/DatabaseReadModelFacade.cs @@ -1,39 +1,46 @@ namespace Evelyn.Core.ReadModel { using System; - using System.Collections.Generic; using System.Threading.Tasks; + using AccountProjects; using Evelyn.Core.ReadModel.EnvironmentDetails; using Evelyn.Core.ReadModel.Infrastructure; using Evelyn.Core.ReadModel.ToggleDetails; using ProjectDetails; - using ProjectList; public class DatabaseReadModelFacade : IReadModelFacade { - private readonly IDatabase _projects; + private readonly IDatabase _accountProjects; - private readonly IDatabase _projectDetails; + private readonly IDatabase _projectDetails; - private readonly IDatabase _environmentDetails; + private readonly IDatabase _environmentDetails; - private readonly IDatabase _toggleDetails; + private readonly IDatabase _toggleDetails; public DatabaseReadModelFacade( - IDatabase projects, - IDatabase projectDetails, - IDatabase environmentDetails, - IDatabase toggleDetails) + IDatabase accountProjects, + IDatabase projectDetails, + IDatabase environmentDetails, + IDatabase toggleDetails) { - _projects = projects; + _accountProjects = accountProjects; _projectDetails = projectDetails; _environmentDetails = environmentDetails; _toggleDetails = toggleDetails; } - public async Task> GetProjects() + public async Task GetProjects(string accountId) { - return await _projects.Get(); + try + { + return await _accountProjects.Get(accountId); + } + catch (NotFoundException) + { + // hack! Remove this + return new AccountProjectsDto(accountId); + } } public async Task GetProjectDetails(Guid projectId) diff --git a/src/Evelyn.Core/ReadModel/EnvironmentDetails/EnvironmentDetailsHandler.cs b/src/Evelyn.Core/ReadModel/EnvironmentDetails/EnvironmentDetailsHandler.cs index 1980ce8..8764054 100644 --- a/src/Evelyn.Core/ReadModel/EnvironmentDetails/EnvironmentDetailsHandler.cs +++ b/src/Evelyn.Core/ReadModel/EnvironmentDetails/EnvironmentDetailsHandler.cs @@ -1,5 +1,6 @@ namespace Evelyn.Core.ReadModel.EnvironmentDetails { + using System; using System.Threading; using System.Threading.Tasks; using CQRSlite.Events; @@ -9,16 +10,16 @@ public class EnvironmentDetailsHandler : ICancellableEventHandler { - private readonly IDatabase _environments; + private readonly IDatabase _db; - public EnvironmentDetailsHandler(IDatabase environments) + public EnvironmentDetailsHandler(IDatabase db) { - _environments = environments; + _db = db; } public Task Handle(EnvironmentAdded message, CancellationToken token) { - _environments.Add(message.EnvironmentId, new EnvironmentDetailsDto(message.Id, message.EnvironmentId, message.Name, message.TimeStamp)); + _db.AddOrUpdate(message.EnvironmentId, new EnvironmentDetailsDto(message.Id, message.EnvironmentId, message.Name, message.TimeStamp)); return Task.CompletedTask; } } diff --git a/src/Evelyn.Core/ReadModel/IReadModelFacade.cs b/src/Evelyn.Core/ReadModel/IReadModelFacade.cs index fb70f75..86fdb9c 100644 --- a/src/Evelyn.Core/ReadModel/IReadModelFacade.cs +++ b/src/Evelyn.Core/ReadModel/IReadModelFacade.cs @@ -1,16 +1,15 @@ namespace Evelyn.Core.ReadModel { using System; - using System.Collections.Generic; using System.Threading.Tasks; - using Evelyn.Core.ReadModel.EnvironmentDetails; - using Evelyn.Core.ReadModel.ToggleDetails; + using AccountProjects; + using EnvironmentDetails; using ProjectDetails; - using ProjectList; + using ToggleDetails; public interface IReadModelFacade { - Task> GetProjects(); + Task GetProjects(string accountId); Task GetProjectDetails(Guid projectId); diff --git a/src/Evelyn.Core/ReadModel/Infrastructure/IDatabase.cs b/src/Evelyn.Core/ReadModel/Infrastructure/IDatabase.cs index 942a852..99bce32 100644 --- a/src/Evelyn.Core/ReadModel/Infrastructure/IDatabase.cs +++ b/src/Evelyn.Core/ReadModel/Infrastructure/IDatabase.cs @@ -1,15 +1,13 @@ namespace Evelyn.Core.ReadModel.Infrastructure { - using System; - using System.Collections.Generic; using System.Threading.Tasks; - public interface IDatabase + public interface IDatabase { - Task> Get(); + Task Get(TKey id); - Task Get(Guid id); + Task AddOrUpdate(TKey key, TValue aggregate); - Task Add(Guid id, T aggregate); + Task Delete(TKey key); } } diff --git a/src/Evelyn.Core/ReadModel/Infrastructure/InMemoryDatabase.cs b/src/Evelyn.Core/ReadModel/Infrastructure/InMemoryDatabase.cs index 8803fa2..0f5270c 100644 --- a/src/Evelyn.Core/ReadModel/Infrastructure/InMemoryDatabase.cs +++ b/src/Evelyn.Core/ReadModel/Infrastructure/InMemoryDatabase.cs @@ -1,23 +1,20 @@ namespace Evelyn.Core.ReadModel.Infrastructure { - using System; using System.Collections.Generic; - using System.Linq; using System.Threading.Tasks; - public class InMemoryDatabase : IDatabase + public class InMemoryDatabase : IDatabase { - private Dictionary _items; + private readonly Dictionary _items; public InMemoryDatabase() { - _items = new Dictionary(); + _items = new Dictionary(); } - public async Task Get(Guid id) + public async Task Get(TKey key) { - T value; - if (!_items.TryGetValue(id, out value)) + if (!_items.TryGetValue(key, out var value)) { throw new NotFoundException(); } @@ -25,14 +22,28 @@ public async Task Get(Guid id) return await Task.FromResult(value); } - public async Task> Get() + public async Task AddOrUpdate(TKey key, TValue value) { - return await Task.FromResult(_items.Values.ToList()); + if (_items.ContainsKey(key)) + { + _items[key] = value; + } + else + { + _items.Add(key, value); + } + + await Task.CompletedTask; } - public async Task Add(Guid id, T item) + public async Task Delete(TKey key) { - _items.Add(id, item); + if (_items.ContainsKey(key)) + { + throw new NotFoundException(); + } + + _items.Remove(key); await Task.CompletedTask; } } diff --git a/src/Evelyn.Core/ReadModel/ProjectDetails/ProjectDetailsHandler.cs b/src/Evelyn.Core/ReadModel/ProjectDetails/ProjectDetailsHandler.cs index 7115411..8cbcc96 100644 --- a/src/Evelyn.Core/ReadModel/ProjectDetails/ProjectDetailsHandler.cs +++ b/src/Evelyn.Core/ReadModel/ProjectDetails/ProjectDetailsHandler.cs @@ -1,5 +1,6 @@ namespace Evelyn.Core.ReadModel.ProjectDetails { + using System; using System.Threading; using System.Threading.Tasks; using CQRSlite.Events; @@ -11,27 +12,27 @@ public class ProjectDetailsHandler : ICancellableEventHandler, ICancellableEventHandler { - private readonly IDatabase _projectDetails; + private readonly IDatabase _db; - public ProjectDetailsHandler(IDatabase projectDetails) + public ProjectDetailsHandler(IDatabase db) { - _projectDetails = projectDetails; + _db = db; } public async Task Handle(ProjectCreated message, CancellationToken token) { - await _projectDetails.Add(message.Id, new ProjectDetailsDto(message.Id, message.Name, message.Version, message.TimeStamp)); + await _db.AddOrUpdate(message.Id, new ProjectDetailsDto(message.Id, message.Name, message.Version, message.TimeStamp)); } public async Task Handle(EnvironmentAdded message, CancellationToken token) { - var projectDetails = await _projectDetails.Get(message.Id); + var projectDetails = await _db.Get(message.Id); projectDetails.AddEnvironment(new EnvironmentListDto(message.EnvironmentId, message.Name), message.TimeStamp, message.Version); } public async Task Handle(ToggleAdded message, CancellationToken token) { - var projectDetails = await _projectDetails.Get(message.Id); + var projectDetails = await _db.Get(message.Id); projectDetails.AddToggle(new ToggleListDto(message.ToggleId, message.Name), message.TimeStamp, message.Version); } } diff --git a/src/Evelyn.Core/ReadModel/ProjectList/ProjectListHandler.cs b/src/Evelyn.Core/ReadModel/ProjectList/ProjectListHandler.cs deleted file mode 100644 index 67dffab..0000000 --- a/src/Evelyn.Core/ReadModel/ProjectList/ProjectListHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Evelyn.Core.ReadModel.ProjectList -{ - using System.Threading; - using System.Threading.Tasks; - using CQRSlite.Events; - using Events; - using Infrastructure; - - public class ProjectListHandler - : ICancellableEventHandler - { - private readonly IDatabase _projects; - - public ProjectListHandler(IDatabase projects) - { - _projects = projects; - } - - public Task Handle(ProjectCreated message, CancellationToken token) - { - _projects.Add(message.Id, new ProjectListDto(message.Id, message.Name)); - return Task.CompletedTask; - } - } -} diff --git a/src/Evelyn.Core/ReadModel/ToggleDetails/ToggleDetailsHandler.cs b/src/Evelyn.Core/ReadModel/ToggleDetails/ToggleDetailsHandler.cs index 0b194d8..b11f030 100644 --- a/src/Evelyn.Core/ReadModel/ToggleDetails/ToggleDetailsHandler.cs +++ b/src/Evelyn.Core/ReadModel/ToggleDetails/ToggleDetailsHandler.cs @@ -1,5 +1,6 @@ namespace Evelyn.Core.ReadModel.ToggleDetails { + using System; using System.Threading; using System.Threading.Tasks; using CQRSlite.Events; @@ -9,16 +10,16 @@ public class ToggleDetailsHandler : ICancellableEventHandler { - private readonly IDatabase _toggles; + private readonly IDatabase _db; - public ToggleDetailsHandler(IDatabase toggles) + public ToggleDetailsHandler(IDatabase db) { - _toggles = toggles; + _db = db; } public Task Handle(ToggleAdded message, CancellationToken token) { - _toggles.Add(message.ToggleId, new ToggleDetailsDto(message.Id, message.ToggleId, message.Name, message.Key, message.TimeStamp)); + _db.AddOrUpdate(message.ToggleId, new ToggleDetailsDto(message.Id, message.ToggleId, message.Name, message.Key, message.TimeStamp)); return Task.CompletedTask; } } diff --git a/src/Evelyn.Management.Api.Rest.IntegrationTests/SmokeTest.cs b/src/Evelyn.Management.Api.Rest.IntegrationTests/SmokeTest.cs index 02aa36e..506d388 100644 --- a/src/Evelyn.Management.Api.Rest.IntegrationTests/SmokeTest.cs +++ b/src/Evelyn.Management.Api.Rest.IntegrationTests/SmokeTest.cs @@ -1,10 +1,12 @@ namespace Evelyn.Management.Api.Rest.IntegrationTests { + using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using AutoFixture; + using Core.ReadModel.AccountProjects; using Core.ReadModel.ProjectDetails; using Core.ReadModel.ProjectList; using Evelyn.Core.ReadModel.EnvironmentDetails; @@ -160,22 +162,21 @@ private void ThenTheResponseHasStatusCode(int expectedStatusCode) private void ThenTheResponseContentIsAnEmptyCollection() { - var response = JsonConvert.DeserializeObject>(_responseContent, DeserializeWithPrivateSetters); - response.Count.Should().Be(0); + var response = JsonConvert.DeserializeObject(_responseContent, DeserializeWithPrivateSetters); + response.Projects.Count.Should().Be(0); } private void ThenTheResponseContentIsACollectionWithOneProject() { - var response = JsonConvert.DeserializeObject>(_responseContent, DeserializeWithPrivateSetters); - response.Count.Should().Be(1); + var response = JsonConvert.DeserializeObject(_responseContent, DeserializeWithPrivateSetters); + response.Projects.Count.Should().Be(1); } private void ThenTheProjectWeAddedIsInTheCollection() { - var projectList = JsonConvert.DeserializeObject>(_responseContent, DeserializeWithPrivateSetters).ToList(); - projectList.Should().Contain(project => - project.Id == _createProjectMessage.Id && - project.Name == _createProjectMessage.Name); + var projectList = JsonConvert.DeserializeObject(_responseContent, DeserializeWithPrivateSetters); + projectList.Projects[_createProjectMessage.Id].Id.Should().Be(_createProjectMessage.Id); + projectList.Projects[_createProjectMessage.Id].Name.Should().Be(_createProjectMessage.Name); } private void ThenTheProjectContainsOneEnvironment() diff --git a/src/Evelyn.Management.Api.Rest.Tests/Read/ProjectsControllerSpecs.cs b/src/Evelyn.Management.Api.Rest.Tests/Read/ProjectsControllerSpecs.cs index f82e240..a0bb126 100644 --- a/src/Evelyn.Management.Api.Rest.Tests/Read/ProjectsControllerSpecs.cs +++ b/src/Evelyn.Management.Api.Rest.Tests/Read/ProjectsControllerSpecs.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using AutoFixture; + using Core.ReadModel.AccountProjects; using Core.ReadModel.ProjectDetails; using Core.ReadModel.ProjectList; using Evelyn.Core.ReadModel; @@ -14,6 +15,7 @@ using Microsoft.AspNetCore.Mvc; using NSubstitute; using NSubstitute.ExceptionExtensions; + using Rest.Write; using TestStack.BDDfy; using Xunit; @@ -22,8 +24,9 @@ public class ProjectsControllerSpecs private readonly Fixture _fixture; private readonly ProjectsController _controller; private readonly IReadModelFacade _readModelFacade; + private readonly string _accountId = Constants.DefaultAccount; private Guid _idOfProjectToGet; - private IEnumerable _projectsReturnedByFacade; + private AccountProjectsDto _accountProjectsReturnedByFacade; private ProjectDetailsDto _projectReturnedByFacade; private ObjectResult _result; @@ -35,26 +38,26 @@ public ProjectsControllerSpecs() } [Fact] - public void GetsAllProjects() + public void GetProjectsOnAccount() { - this.Given(_ => GivenThatThereAreProjects()) - .When(_ => WhenWeGetAllTheProjects()) + this.Given(_ => GivenThatThereAreProjectsOnAnAccount()) + .When(_ => WhenWeGetTheAccountProjects()) .Then(_ => ThenStatusCode200IsReturned()) .And(_ => ThenAllProjectsAreReturned()) .BDDfy(); } [Fact] - public void ExceptionWhenGettingProjects() + public void ExceptionWhenGettingProjectOnAccount() { this.Given(_ => GivenThatAnExceptionIsThrownByHandlerWhenGettingProjects()) - .When(_ => WhenWeGetAllTheProjects()) + .When(_ => WhenWeGetTheAccountProjects()) .Then(_ => ThenStatusCode500IsReturned()) .BDDfy(); } [Fact] - public void GetsProject() + public void GetProjectDetails() { this.Given(_ => GivenTheProjectWeWantDoesExist()) .When(_ => WhenWeGetTheProject()) @@ -64,7 +67,7 @@ public void GetsProject() } [Fact] - public void ProjectNotFound() + public void ProjectDetailsNotFound() { this.Given(_ => GivenTheProjectWeWantDoesntExist()) .When(_ => WhenWeGetTheProject()) @@ -73,7 +76,7 @@ public void ProjectNotFound() } [Fact] - public void ExceptionWhenGettingProject() + public void ExceptionWhenGettingProjectDetails() { this.Given(_ => GivenThatAnExceptionIsThrownWhenGettingProject()) .When(_ => WhenWeGetTheProject()) @@ -81,17 +84,23 @@ public void ExceptionWhenGettingProject() .BDDfy(); } - private void GivenThatThereAreProjects() + private void GivenThatThereAreProjectsOnAnAccount() { - _projectsReturnedByFacade = _fixture.CreateMany(); + _accountProjectsReturnedByFacade = new AccountProjectsDto(_accountId); - _readModelFacade.GetProjects().Returns(_projectsReturnedByFacade); + var projects = _fixture.CreateMany(); + foreach (var project in projects) + { + _accountProjectsReturnedByFacade.Projects.Add(project.Id, project); + } + + _readModelFacade.GetProjects(_accountId).Returns(_accountProjectsReturnedByFacade); } private void GivenThatAnExceptionIsThrownByHandlerWhenGettingProjects() { _readModelFacade - .GetProjects() + .GetProjects(_accountId) .Throws(_fixture.Create()); } @@ -120,7 +129,7 @@ private void GivenThatAnExceptionIsThrownWhenGettingProject() .Throws(_fixture.Create()); } - private async Task WhenWeGetAllTheProjects() + private async Task WhenWeGetTheAccountProjects() { _result = await _controller.Get(); } @@ -147,9 +156,8 @@ private void ThenStatusCode500IsReturned() private void ThenAllProjectsAreReturned() { - var returnedProjects = (_result.Value as IEnumerable).ToList(); - - returnedProjects.Should().Equal(_projectsReturnedByFacade); + var accountProjectsDto = _result.Value as AccountProjectsDto; + accountProjectsDto.Should().Be(_accountProjectsReturnedByFacade); } private void ThenTheExpectedProjectIsReturned() diff --git a/src/Evelyn.Management.Api.Rest/Read/ProjectsController.cs b/src/Evelyn.Management.Api.Rest/Read/ProjectsController.cs index 27b44fe..0d66304 100644 --- a/src/Evelyn.Management.Api.Rest/Read/ProjectsController.cs +++ b/src/Evelyn.Management.Api.Rest/Read/ProjectsController.cs @@ -7,10 +7,11 @@ using Evelyn.Core.ReadModel; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; + using Write; [Route("api/projects")] [ProducesResponseType(typeof(IDictionary), StatusCodes.Status500InternalServerError)] - public class ProjectsController : Controller + public class ProjectsController : EvelynController { private readonly IReadModelFacade _readModelFacade; @@ -25,7 +26,7 @@ public async Task Get() { try { - var result = await _readModelFacade.GetProjects(); + var result = await _readModelFacade.GetProjects(AccountId); return Ok(result); } catch (Exception)