diff --git a/PinkParrot.sln b/PinkParrot.sln index 6296fae50..58de44fd2 100644 --- a/PinkParrot.sln +++ b/PinkParrot.sln @@ -32,6 +32,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_write", "pinkpar EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Write", "src\pinkparrot_write\PinkParrot.Write\PinkParrot.Write.xproj", "{A85201C6-6AF8-4B63-8365-08F741050438}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Read", "src\pinkparrot_read\PinkParrot.Read\PinkParrot.Read.xproj", "{A92B4734-2587-4F6F-97A3-741BE48709A5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_read", "pinkparrot_read", "{2CDAF493-B88E-48BD-9634-B5128C106199}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,6 +70,10 @@ Global {A85201C6-6AF8-4B63-8365-08F741050438}.Debug|Any CPU.Build.0 = Debug|Any CPU {A85201C6-6AF8-4B63-8365-08F741050438}.Release|Any CPU.ActiveCfg = Release|Any CPU {A85201C6-6AF8-4B63-8365-08F741050438}.Release|Any CPU.Build.0 = Release|Any CPU + {A92B4734-2587-4F6F-97A3-741BE48709A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A92B4734-2587-4F6F-97A3-741BE48709A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A92B4734-2587-4F6F-97A3-741BE48709A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A92B4734-2587-4F6F-97A3-741BE48709A5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -78,5 +86,6 @@ Global {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {25F66C64-058A-4D44-BC0C-F12A054F9A91} = {6AE39761-FD74-45CD-99CF-73D3D2E5D064} {A85201C6-6AF8-4B63-8365-08F741050438} = {4AED438F-684F-4FAE-B016-21CF2EAEA79F} + {A92B4734-2587-4F6F-97A3-741BE48709A5} = {2CDAF493-B88E-48BD-9634-B5128C106199} EndGlobalSection EndGlobal diff --git a/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs b/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs new file mode 100644 index 000000000..08929ad0e --- /dev/null +++ b/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs @@ -0,0 +1,74 @@ +// ========================================================================== +// SchemaFieldsController.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using PinkParrot.Infrastructure.CQRS.Commands; +using PinkParrot.Write.Schema.Commands; + +namespace PinkParrot.Modules.Api.Schemas +{ + public class SchemasFieldsController : Controller + { + private readonly ICommandBus commandBus; + + public SchemasFieldsController(ICommandBus commandBus) + { + this.commandBus = commandBus; + } + + [HttpPost] + [Route("schemas/{name}/fields/")] + public Task Add(AddModelField command) + { + return commandBus.PublishAsync(command); + } + + [HttpPut] + [Route("schemas/{name}/fields/{fieldId:long}/")] + public Task Update(UpdateModelField command) + { + return commandBus.PublishAsync(command); + } + + [HttpPut] + [Route("schemas/{name}/fields/{fieldId:long}/hide/")] + public Task Hide(HideModelField command) + { + return commandBus.PublishAsync(command); + } + + [HttpPut] + [Route("schemas/{name}/fields/{fieldId:long}/show/")] + public Task Show(ShowModelField command) + { + return commandBus.PublishAsync(command); + } + + [HttpPut] + [Route("schemas/{name}/fields/{fieldId:long}/enable/")] + public Task Enable(EnableModelField command) + { + return commandBus.PublishAsync(command); + } + + [HttpPut] + [Route("schemas/{name}/fields/{fieldId:long}/disable/")] + public Task Enable(DisableModelField command) + { + return commandBus.PublishAsync(command); + } + + [HttpDelete] + [Route("schemas/{name}/fields/{fieldId:long}/")] + public Task Delete(DeleteModelField command) + { + return commandBus.PublishAsync(command); + } + } +} \ No newline at end of file diff --git a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs index 750e5be11..833fe5ee0 100644 --- a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs +++ b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs @@ -1,26 +1,55 @@ -using System.Threading.Tasks; +// ========================================================================== +// SchemasController.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using PinkParrot.Infrastructure.CQRS.Commands; +using PinkParrot.Read.Models; +using PinkParrot.Write.Schema.Commands; namespace PinkParrot.Modules.Api.Schemas { public class SchemasController : Controller { + private readonly ICommandBus commandBus; + + public SchemasController(ICommandBus commandBus) + { + this.commandBus = commandBus; + } + + [HttpGet] + [Route("schemas/")] + public Task> Query() + { + return null; + } + [HttpPost] [Route("schemas/")] - public async Task Create() + public Task Create(CreateModelSchema command) { + return commandBus.PublishAsync(command); } [HttpPut] [Route("schemas/{name}/")] - public async Task Update() + public Task Update(UpdateModelSchema command) { + return commandBus.PublishAsync(command); } [HttpDelete] [Route("schemas/{name}/")] - public async Task Delete() + public Task Delete() { + return commandBus.PublishAsync(new DeleteModelSchema()); } } } \ No newline at end of file diff --git a/src/PinkParrot/Startup.cs b/src/PinkParrot/Startup.cs index 3bcbdb1a0..56b7fa74e 100644 --- a/src/PinkParrot/Startup.cs +++ b/src/PinkParrot/Startup.cs @@ -6,27 +6,59 @@ // All rights reserved. // ========================================================================== +using System; +using Autofac; +using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using PinkParrot.Infrastructure.CQRS.Autofac; +using PinkParrot.Infrastructure.CQRS.Commands; +// ReSharper disable AccessToModifiedClosure namespace PinkParrot { public class Startup { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) + public IServiceProvider ConfigureServices(IServiceCollection services) { - } + services.AddMvc(); + services.AddRouting(); + services.AddSwaggerGen(); + + IContainer container = null; + + var containerBuilder = new ContainerBuilder(); + containerBuilder.Populate(services); + + containerBuilder.Register(c => container) + .As() + .SingleInstance(); - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + containerBuilder.RegisterType() + .As() + .SingleInstance(); + + containerBuilder.RegisterType() + .As() + .SingleInstance(); + + container = containerBuilder.Build(); + + return new AutofacServiceProvider(container); + } + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); + app.UseMvc(); + app.UseStaticFiles(); + app.UseSwagger(); + app.UseSwaggerUi(); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); diff --git a/src/PinkParrot/project.json b/src/PinkParrot/project.json index 41a3470bb..98df8f0b6 100644 --- a/src/PinkParrot/project.json +++ b/src/PinkParrot/project.json @@ -1,5 +1,7 @@ { "dependencies": { + "Autofac": "4.1.0", + "Autofac.Extensions.DependencyInjection": "4.0.0", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Razor.Tools": { @@ -19,10 +21,14 @@ "version": "1.0.0", "type": "platform" }, + "MongoDB.Driver": "2.3.0-rc1", "PinkParrot.Core": "1.0.0-*", "PinkParrot.Events": "1.0.0-*", "PinkParrot.Infrastructure": "1.0.0-*", - "PinkParrot.Write": "1.0.0-*" + "PinkParrot.Read": "1.0.0-*", + "PinkParrot.Write": "1.0.0-*", + "Swashbuckle.SwaggerGen": "6.0.0-beta901", + "Swashbuckle.SwaggerUi": "6.0.0-beta901" }, "tools": { diff --git a/src/PinkParrot/web.config b/src/PinkParrot/web.config index dc0514fca..05ac0e36d 100644 --- a/src/PinkParrot/web.config +++ b/src/PinkParrot/web.config @@ -1,10 +1,5 @@  - - - diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs index ad4c39d26..c13f9fe79 100644 --- a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs +++ b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelField.cs @@ -134,7 +134,7 @@ namespace PinkParrot.Core.Schema { if (isHidden) { - throw new DomainValidationException($"The field '{name} is already hidden."); + throw new ValidationException($"The field '{name} is already hidden."); } var clone = Clone(); @@ -148,7 +148,7 @@ namespace PinkParrot.Core.Schema { if (!isHidden) { - throw new DomainValidationException($"The field '{name} is already visible."); + throw new ValidationException($"The field '{name} is already visible."); } var clone = Clone(); @@ -162,7 +162,7 @@ namespace PinkParrot.Core.Schema { if (isDisabled) { - throw new DomainValidationException($"The field '{name} is already disabled."); + throw new ValidationException($"The field '{name} is already disabled."); } var clone = Clone(); @@ -176,7 +176,7 @@ namespace PinkParrot.Core.Schema { if (!isDisabled) { - throw new DomainValidationException($"The field '{name} is already enabled."); + throw new ValidationException($"The field '{name} is already enabled."); } var clone = Clone(); diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs index b45f13bb2..cbacfc349 100644 --- a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs +++ b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchema.cs @@ -37,7 +37,7 @@ namespace PinkParrot.Core.Schema { if (!name.IsSlug()) { - throw new DomainValidationException("Cannot create the schema.", $"'{name}' is not a valid slug."); + throw new ValidationException("Cannot create the schema.", $"'{name}' is not a valid slug."); } return new ModelSchema(new ModelSchemaMetadata(name), ImmutableDictionary.Empty); @@ -79,7 +79,7 @@ namespace PinkParrot.Core.Schema if (errors.Any()) { - throw new DomainValidationException($"Cannot update field with id '{fieldId}', becase the settings are invalid.", errors); + throw new ValidationException($"Cannot update field with id '{fieldId}', becase the settings are invalid.", errors); } return newField; @@ -112,7 +112,7 @@ namespace PinkParrot.Core.Schema if (fields.Values.Any(f => f.Name == field.Name && f.Id != field.Id)) { - throw new DomainValidationException($"A field with name '{field.Name}' already exists."); + throw new ValidationException($"A field with name '{field.Name}' already exists."); } return new ModelSchema(metadata, fields.SetItem(field.Id, field)); @@ -122,7 +122,7 @@ namespace PinkParrot.Core.Schema { if (!fields.ContainsKey(fieldId)) { - throw new DomainValidationException($"A field with id {fieldId} does not exist."); + throw new ValidationException($"A field with id {fieldId} does not exist."); } return new ModelSchema(metadata, fields.Remove(fieldId)); @@ -134,7 +134,7 @@ namespace PinkParrot.Core.Schema if (!fields.TryGetValue(fieldId, out field)) { - throw new DomainValidationException($"Cannot update field with id '{fieldId}'.", "Field does not exist."); + throw new ValidationException($"Cannot update field with id '{fieldId}'.", "Field does not exist."); } var newField = updater(field); @@ -168,7 +168,7 @@ namespace PinkParrot.Core.Schema if (errors.Any()) { - throw new DomainValidationException("The data is not valid.", errors); + throw new ValidationException("The data is not valid.", errors); } } } diff --git a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaMetadata.cs b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaMetadata.cs index 63ee493a4..a767d7320 100644 --- a/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaMetadata.cs +++ b/src/pinkparrot_core/PinkParrot.Core/Schema/ModelSchemaMetadata.cs @@ -54,7 +54,7 @@ namespace PinkParrot.Core.Schema { if (!newName.IsSlug()) { - throw new DomainValidationException("Cannot update the schema.", $"'{newName}' is not a valid slug."); + throw new ValidationException("Cannot update the schema.", $"'{newName}' is not a valid slug."); } clone.name = newName; diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs index 34b447473..f74488f4f 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldAdded.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelFieldAdded : IEvent + public class ModelFieldAdded : TenantEvent { public long FieldId; diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs index fa4f616fd..c131b4ff5 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDeleted.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelFieldDeleted : IEvent + public class ModelFieldDeleted : TenantEvent { public long FieldId; } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs index 9ced16879..b48984d21 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldDisabled.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelFieldDisabled : IEvent + public class ModelFieldDisabled : TenantEvent { public long FieldId; } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs index 8c3a777bc..37bb0d1c2 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldEnabled.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelFieldEnabled : IEvent + public class ModelFieldEnabled : TenantEvent { public long FieldId; } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs index 371572f54..671257b63 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldHidden.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelFieldHidden : IEvent + public class ModelFieldHidden : TenantEvent { public long FieldId; } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs index af1714a43..06a0851ac 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldShown.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelFieldShown : IEvent + public class ModelFieldShown : TenantEvent { public long FieldId; } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs index a7071bcd0..881346f9d 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelFieldUpdated.cs @@ -7,11 +7,10 @@ // ========================================================================== using PinkParrot.Infrastructure; -using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schema { - public class ModelFieldUpdated : IEvent + public class ModelFieldUpdated : TenantEvent { public long FieldId; diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs index 1e60a854c..013659478 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaCreated.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelSchemaCreated : IEvent + public class ModelSchemaCreated : TenantEvent { public string Name; } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs index 4e67bfbf9..78938048a 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaDeleted.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using PinkParrot.Infrastructure.CQRS.Events; - namespace PinkParrot.Events.Schema { - public class ModelSchemaDeleted : IEvent + public class ModelSchemaDeleted : TenantEvent { } } diff --git a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs index c4d345e89..638028bd7 100644 --- a/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs +++ b/src/pinkparrot_events/PinkParrot.Events/Schema/ModelSchemaUpdated.cs @@ -7,11 +7,10 @@ // ========================================================================== using PinkParrot.Infrastructure; -using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schema { - public class ModelSchemaUpdated : IEvent + public class ModelSchemaUpdated : TenantEvent { public string NewName; diff --git a/src/pinkparrot_events/PinkParrot.Events/TenantEvent.cs b/src/pinkparrot_events/PinkParrot.Events/TenantEvent.cs new file mode 100644 index 000000000..60a1fef05 --- /dev/null +++ b/src/pinkparrot_events/PinkParrot.Events/TenantEvent.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// TenantEvent.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using PinkParrot.Infrastructure.CQRS.Events; + +namespace PinkParrot.Events +{ + public class TenantEvent : IEvent + { + public Guid TenantId; + } +} diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs index ec89a3811..37619d378 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/DispatchingTests.cs @@ -10,6 +10,9 @@ using System.Threading.Tasks; using PinkParrot.Infrastructure.Dispatching; using PinkParrot.Infrastructure.Tasks; using Xunit; +// ReSharper disable UnusedMethodReturnValue.Local +// ReSharper disable UnusedParameter.Local +// ReSharper disable UnusedMember.Local namespace PinkParrot.Infrastructure { diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs index 31d214887..23d7511fc 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs @@ -295,6 +295,14 @@ namespace PinkParrot.Infrastructure AssertBoolean(false); } + [Fact] + public void Should_provide_value_as_string() + { + bag.Set("Key", "Foo"); + + AssertString("Foo"); + } + [Fact] public void Should_throw_when_converting_instant_to_number() { diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Autofac/AutofacDomainObjectFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Autofac/AutofacDomainObjectFactory.cs new file mode 100644 index 000000000..8fbf33ee0 --- /dev/null +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/Autofac/AutofacDomainObjectFactory.cs @@ -0,0 +1,33 @@ +// ========================================================================== +// AutofacDomainObjectFactory.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using Autofac; +using PinkParrot.Infrastructure.CQRS.Commands; + +namespace PinkParrot.Infrastructure.CQRS.Autofac +{ + public sealed class AutofacDomainObjectFactory : IDomainObjectFactory + { + private readonly IContainer container; + + public AutofacDomainObjectFactory(IContainer container) + { + Guard.NotNull(container, nameof(container)); + + this.container = container; + } + + public IAggregate CreateNew(Type type, Guid id) + { + return (IAggregate)container.Resolve(type, + new NamedParameter("id", id), + new NamedParameter("version", 0)); + } + } +} diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs index d10c1ec32..aaec4cacc 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/DomainObject.cs @@ -38,17 +38,19 @@ namespace PinkParrot.Infrastructure.CQRS this.version = version; } - protected abstract void ApplyEvent(IEvent @event); + protected abstract void ApplyEvent(Envelope @event); protected void RaiseEvent(Envelope envelope, bool disableApply = false) where TEvent : class, IEvent { Guard.NotNull(envelope, nameof(envelope)); - uncomittedEvents.Add(envelope.To()); + var envelopeToAdd = envelope.To(); + + uncomittedEvents.Add(envelopeToAdd); if (!disableApply) { - ApplyEvent(envelope.Payload); + ApplyEvent(envelopeToAdd); } } @@ -56,15 +58,17 @@ namespace PinkParrot.Infrastructure.CQRS { Guard.NotNull(@event, nameof(@event)); - uncomittedEvents.Add(EnvelopeFactory.ForEvent(@event, id)); + var envelopeToAdd = EnvelopeFactory.ForEvent(@event, id); + + uncomittedEvents.Add(envelopeToAdd); if (!disableApply) { - ApplyEvent(@event); + ApplyEvent(envelopeToAdd); } } - void IAggregate.ApplyEvent(IEvent @event) + void IAggregate.ApplyEvent(Envelope @event) { ApplyEvent(@event); version++; } diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeExtensions.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeExtensions.cs index 5d384486b..fa7499dba 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeExtensions.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/EnvelopeExtensions.cs @@ -14,9 +14,9 @@ namespace PinkParrot.Infrastructure.CQRS { public static class EnvelopeExtensions { - public static int EventNumber(this Envelope envelope) where T : class + public static int EventNumber(this EnvelopeHeaders headers) { - return envelope.Headers[CommonHeaders.EventNumber].ToInt32(CultureInfo.InvariantCulture); + return headers[CommonHeaders.EventNumber].ToInt32(CultureInfo.InvariantCulture); } public static Envelope SetEventNumber(this Envelope envelope, int value) where T : class @@ -26,9 +26,9 @@ namespace PinkParrot.Infrastructure.CQRS return envelope; } - public static Guid CommitId(this Envelope envelope) where T : class + public static Guid CommitId(this EnvelopeHeaders headers) { - return envelope.Headers[CommonHeaders.CommitId].ToGuid(CultureInfo.InvariantCulture); + return headers[CommonHeaders.CommitId].ToGuid(CultureInfo.InvariantCulture); } public static Envelope SetCommitId(this Envelope envelope, Guid value) where T : class @@ -38,9 +38,9 @@ namespace PinkParrot.Infrastructure.CQRS return envelope; } - public static Guid AggregateId(this Envelope envelope) where T : class + public static Guid AggregateId(this EnvelopeHeaders headers) { - return envelope.Headers[CommonHeaders.AggregateId].ToGuid(CultureInfo.InvariantCulture); + return headers[CommonHeaders.AggregateId].ToGuid(CultureInfo.InvariantCulture); } public static Envelope SetAggregateId(this Envelope envelope, Guid value) where T : class @@ -50,9 +50,9 @@ namespace PinkParrot.Infrastructure.CQRS return envelope; } - public static Guid EventId(this Envelope envelope) where T : class + public static Guid EventId(this EnvelopeHeaders headers) { - return envelope.Headers[CommonHeaders.EventId].ToGuid(CultureInfo.InvariantCulture); + return headers[CommonHeaders.EventId].ToGuid(CultureInfo.InvariantCulture); } public static Envelope SetEventId(this Envelope envelope, Guid value) where T : class @@ -62,9 +62,9 @@ namespace PinkParrot.Infrastructure.CQRS return envelope; } - public static Instant Timestamp(this Envelope envelope) where T : class + public static Instant Timestamp(this EnvelopeHeaders headers) { - return envelope.Headers[CommonHeaders.Timestamp].ToInstant(CultureInfo.InvariantCulture); + return headers[CommonHeaders.Timestamp].ToInstant(CultureInfo.InvariantCulture); } public static Envelope SetTimestamp(this Envelope envelope, Instant value) where T : class diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/GetEventStore/GetEventStoreDomainObjectRepository.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/GetEventStore/GetEventStoreDomainObjectRepository.cs new file mode 100644 index 000000000..dc2d69950 --- /dev/null +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/GetEventStore/GetEventStoreDomainObjectRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace PinkParrot.Infrastructure.CQRS.GetEventStore +{ + public class GetEventStoreDomainObjectRepository + { + } +} diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs index 8d71829ee..ea9dbebe3 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CQRS/IAggregate.cs @@ -18,7 +18,7 @@ namespace PinkParrot.Infrastructure.CQRS int Version { get; } - void ApplyEvent(IEvent @event); + void ApplyEvent(Envelope @event); void ClearUncommittedEvents(); diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs index e9d7aa153..800e343fe 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionContextDispatcherFactory.cs @@ -27,6 +27,7 @@ namespace PinkParrot.Infrastructure.Dispatching return new Tuple>(inputType, (Action)handler); } + // ReSharper disable once UnusedMember.Local private static Action Factory(MethodInfo methodInfo) { var type = typeof(Action); diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs index 1222e4419..c42fb1550 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/ActionDispatcherFactory.cs @@ -27,6 +27,7 @@ namespace PinkParrot.Infrastructure.Dispatching return new Tuple>(inputType, (Action)handler); } + // ReSharper disable once UnusedMember.Local private static Action Factory(MethodInfo methodInfo) { var type = typeof(Action); diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs index 0849d245e..448afec09 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncContextDispatcherFactory.cs @@ -27,6 +27,7 @@ namespace PinkParrot.Infrastructure.Dispatching return new Tuple>(inputType, (Func)handler); } + // ReSharper disable once UnusedMember.Local private static Func Factory(MethodInfo methodInfo) { var type = typeof(Func); diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs index 797816c4c..ea7cbdba1 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Dispatching/FuncDispatcherFactory.cs @@ -27,6 +27,7 @@ namespace PinkParrot.Infrastructure.Dispatching return new Tuple>(inputType, (Func)handler); } + // ReSharper disable once UnusedMember.Local private static Func Factory(MethodInfo methodInfo) { var type = typeof(Func); diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs deleted file mode 100644 index c1aad3861..000000000 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ICommandContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace PinkParrot.Infrastructure.CQRS.Commands -{ - public interface ICommandContext - { - ICommand Command { get; } - Exception Exception { get; } - IDomainObjectFactory Factory { get; } - bool IsFailed { get; } - bool IsHandled { get; } - bool IsSucceeded { get; } - IDomainObjectRepository Repository { get; } - } -} \ No newline at end of file diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainValidationException.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ValidationException.cs similarity index 70% rename from src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainValidationException.cs rename to src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ValidationException.cs index 5b3ea411a..eb32706b8 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/DomainValidationException.cs +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/ValidationException.cs @@ -1,5 +1,5 @@ // ========================================================================== -// DomainValidationException.cs +// ValidationException.cs // PinkParrot Headless CMS // ========================================================================== // Copyright (c) PinkParrot Group @@ -12,7 +12,7 @@ using System.Linq; namespace PinkParrot.Infrastructure { - public class DomainValidationException : Exception + public class ValidationException : Exception { private readonly IReadOnlyList errors; @@ -21,25 +21,25 @@ namespace PinkParrot.Infrastructure get { return errors; } } - public DomainValidationException(string message, params string[] errors) + public ValidationException(string message, params string[] errors) : base(message) { this.errors = errors != null ? errors.ToList() : new List(); } - public DomainValidationException(string message, IReadOnlyList errors) + public ValidationException(string message, IReadOnlyList errors) : base(message) { this.errors = errors ?? new List(); } - public DomainValidationException(string message, Exception inner, params string[] errors) + public ValidationException(string message, Exception inner, params string[] errors) : base(message, inner) { this.errors = errors != null ? errors.ToList() : new List(); } - public DomainValidationException(string message, Exception inner, IReadOnlyList errors) + public ValidationException(string message, Exception inner, IReadOnlyList errors) : base(message, inner) { this.errors = errors ?? new List(); diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json index e07b8b190..d650396e4 100644 --- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json +++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/project.json @@ -1,11 +1,12 @@ { "version": "1.0.0-*", "dependencies": { - "NodaTime": "2.0.0-alpha20160729", + "Autofac": "4.1.0", "NETStandard.Library": "1.6.0", - "System.Reflection.TypeExtensions": "4.1.0", + "Newtonsoft.Json": "9.0.1", + "NodaTime": "2.0.0-alpha20160729", "System.Linq": "4.1.0", - "Newtonsoft.Json": "9.0.1" + "System.Reflection.TypeExtensions": "4.1.0" }, "frameworks": { "netcoreapp1.0": { diff --git a/src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaRM.cs b/src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaRM.cs new file mode 100644 index 000000000..3f9d4e730 --- /dev/null +++ b/src/pinkparrot_read/PinkParrot.Read/Models/ModelSchemaRM.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace PinkParrot.Read.Models +{ + public sealed class ModelSchemaRM + { + public string Id { get; set; } + + public string Name { get; set; } + + public Guid SchemaId { get; set; } + } +} diff --git a/src/pinkparrot_read/PinkParrot.Read/PinkParrot.Read.xproj b/src/pinkparrot_read/PinkParrot.Read/PinkParrot.Read.xproj new file mode 100644 index 000000000..64f6264dc --- /dev/null +++ b/src/pinkparrot_read/PinkParrot.Read/PinkParrot.Read.xproj @@ -0,0 +1,19 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + a92b4734-2587-4f6f-97a3-741be48709a5 + PinkParrot.Read + .\obj + .\bin\ + + + + 2.0 + + + \ No newline at end of file diff --git a/src/pinkparrot_read/PinkParrot.Read/Repositories/IModelSchemaRepository.cs b/src/pinkparrot_read/PinkParrot.Read/Repositories/IModelSchemaRepository.cs new file mode 100644 index 000000000..73aa42957 --- /dev/null +++ b/src/pinkparrot_read/PinkParrot.Read/Repositories/IModelSchemaRepository.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// IModelSchemaRepository.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Threading.Tasks; +using PinkParrot.Read.Models; + +namespace PinkParrot.Read.Repositories +{ + public interface IModelSchemaRepository + { + Task> QueryAllAsync(); + } +} diff --git a/src/pinkparrot_read/PinkParrot.Read/Repositories/MongoDb/MongoDbModelSchemaRepository.cs b/src/pinkparrot_read/PinkParrot.Read/Repositories/MongoDb/MongoDbModelSchemaRepository.cs new file mode 100644 index 000000000..dad9a3c29 --- /dev/null +++ b/src/pinkparrot_read/PinkParrot.Read/Repositories/MongoDb/MongoDbModelSchemaRepository.cs @@ -0,0 +1,42 @@ +// ========================================================================== +// MongoDbModelSchemaRepository.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver; +using PinkParrot.Read.Models; +using PinkParrot.Read.Repositories.MongoDb.Utils; + +namespace PinkParrot.Read.Repositories.MongoDb +{ + public sealed class MongoDbModelSchemaRepository : BaseRepository, IModelSchemaRepository + { + public MongoDbModelSchemaRepository(IMongoDatabase database) + : base(database, "ModelSchemas") + { + CreateIndicesAsync().Wait(); + } + + private async Task CreateIndicesAsync() + { + await Collection.Indexes.CreateOneAsync(IndexKeys.Ascending(x => x.SchemaId)); + } + + public IQueryable QuerySchemas() + { + return Collection.AsQueryable(); + } + + public Task> QueryAllAsync() + { + return Collection.Find(s => true).ToListAsync(); + } + } +} diff --git a/src/pinkparrot_read/PinkParrot.Read/Repositories/MongoDb/Utils/BaseRepository.cs b/src/pinkparrot_read/PinkParrot.Read/Repositories/MongoDb/Utils/BaseRepository.cs new file mode 100644 index 000000000..b87fd6248 --- /dev/null +++ b/src/pinkparrot_read/PinkParrot.Read/Repositories/MongoDb/Utils/BaseRepository.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MongoDB.Driver; +using PinkParrot.Infrastructure; + +namespace PinkParrot.Read.Repositories.MongoDb.Utils +{ + public abstract class BaseRepository + { + private readonly IMongoCollection collection; + private readonly IndexKeysDefinitionBuilder indexKeys = new IndexKeysDefinitionBuilder(); + + protected IMongoCollection Collection + { + get { return collection; } + } + + protected IndexKeysDefinitionBuilder IndexKeys + { + get { return indexKeys; } + } + + protected BaseRepository(IMongoDatabase database, string collectioName) + { + Guard.NotNull(database, nameof(database)); + Guard.NotNullOrEmpty(collectioName, nameof(collectioName)); + + collection = database.GetCollection(collectioName); + } + } +} diff --git a/src/pinkparrot_read/PinkParrot.Read/project.json b/src/pinkparrot_read/PinkParrot.Read/project.json new file mode 100644 index 000000000..3b0c5a5d0 --- /dev/null +++ b/src/pinkparrot_read/PinkParrot.Read/project.json @@ -0,0 +1,27 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "MongoDB.Driver": "2.3.0-rc1", + "NETStandard.Library": "1.6.0", + "NodaTime": "2.0.0-alpha20160729", + "PinkParrot.Core": "1.0.0-*", + "PinkParrot.Events": "1.0.0-*", + "PinkParrot.Infrastructure": "1.0.0-*" + }, + + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" + } + } + } + }, + + "tooling": { + "defaultNamespace": "PinkParrot.Read" + } +} diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/ISchemaIdProvider.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/ISchemaIdProvider.cs new file mode 100644 index 000000000..9fbe52e39 --- /dev/null +++ b/src/pinkparrot_write/PinkParrot.Write/Schema/ISchemaIdProvider.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// ISchemaIdProvider.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Threading.Tasks; + +namespace PinkParrot.Write.Schema +{ + public interface ISchemaIdProvider + { + Task FindSchemaId(string name); + } +} diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs index 2b7b1e882..70bb100e1 100644 --- a/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs +++ b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaDomainObject.cs @@ -199,9 +199,9 @@ namespace PinkParrot.Write.Schema } } - protected override void ApplyEvent(IEvent @event) + protected override void ApplyEvent(Envelope @event) { - this.DispatchAction(@event); + this.DispatchAction(@event.Payload); } } } \ No newline at end of file diff --git a/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaNameMap.cs b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaNameMap.cs new file mode 100644 index 000000000..92d83cead --- /dev/null +++ b/src/pinkparrot_write/PinkParrot.Write/Schema/ModelSchemaNameMap.cs @@ -0,0 +1,70 @@ +// ========================================================================== +// ModelSchemaNameMap.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using PinkParrot.Events.Schema; +using PinkParrot.Infrastructure.CQRS; +using PinkParrot.Infrastructure.CQRS.Events; +using PinkParrot.Infrastructure.Dispatching; +// ReSharper disable InvertIf + +namespace PinkParrot.Write.Schema +{ + public class ModelSchemaNameMap : DomainObject + { + private readonly Dictionary schemaIdsByName = new Dictionary(); + private readonly Dictionary schemaNamesByIds = new Dictionary(); + + public ModelSchemaNameMap(Guid id, int version) + : base(id, version) + { + } + + protected void On(ModelSchemaDeleted @event, EnvelopeHeaders headers) + { + var aggregateId = headers.AggregateId(); + + string oldName; + + if (schemaNamesByIds.TryGetValue(aggregateId, out oldName)) + { + schemaIdsByName.Remove(oldName); + schemaNamesByIds.Remove(aggregateId); + } + } + + protected void On(ModelSchemaUpdated @event, EnvelopeHeaders headers) + { + var aggregateId = headers.AggregateId(); + + string oldName; + + if (schemaNamesByIds.TryGetValue(aggregateId, out oldName)) + { + schemaIdsByName.Remove(oldName); + schemaIdsByName[@event.NewName] = aggregateId; + } + } + + protected void On(ModelSchemaCreated @event, EnvelopeHeaders headers) + { + schemaIdsByName[@event.Name] = headers.AggregateId(); + } + + public void Apply(Envelope @event) + { + ApplyEvent(@event); + } + + protected override void ApplyEvent(Envelope @event) + { + this.DispatchAction(@event.Payload, @event.Headers); + } + } +}