diff --git a/PinkParrot.sln b/PinkParrot.sln index 43bff9ab8..6e353e33c 100644 --- a/PinkParrot.sln +++ b/PinkParrot.sln @@ -18,8 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_domain", "pinkpa EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Core", "src\PinkParrot.Core\PinkParrot.Core.xproj", "{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Infrastructure.Tests", "src\PinkParrot.Infrastructure.Tests\PinkParrot.Infrastructure.Tests.xproj", "{840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}" -EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Infrastructure", "src\PinkParrot.Infrastructure\PinkParrot.Infrastructure.xproj", "{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Events", "src\PinkParrot.Events\PinkParrot.Events.xproj", "{25F66C64-058A-4D44-BC0C-F12A054F9A91}" @@ -32,6 +30,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Store.MongoDb", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pinkparrot_store", "pinkparrot_store", "{4082AA58-D410-4C52-8E88-3B0A4E860B28}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Write.Tests", "tests\PinkParrot.Write.Tests\PinkParrot.Write.Tests.xproj", "{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "PinkParrot.Infrastructure.Tests", "tests\PinkParrot.Infrastructure.Tests\PinkParrot.Infrastructure.Tests.xproj", "{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,10 +48,6 @@ Global {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.Build.0 = Debug|Any CPU {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.ActiveCfg = Release|Any CPU {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.Build.0 = Release|Any CPU - {840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5}.Release|Any CPU.Build.0 = Release|Any CPU {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.Build.0 = Debug|Any CPU {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -70,6 +68,14 @@ Global {28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Release|Any CPU.Build.0 = Release|Any CPU + {9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Release|Any CPU.Build.0 = Release|Any CPU + {7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -77,11 +83,12 @@ Global GlobalSection(NestedProjects) = preSolution {61F6BBCE-A080-4400-B194-70E2F5D2096E} = {24A3171D-2905-49C9-8A49-A473799014E8} {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} - {840C02B1-48F8-4C8A-8862-8A3FDEFDE8D5} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {25F66C64-058A-4D44-BC0C-F12A054F9A91} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} {A85201C6-6AF8-4B63-8365-08F741050438} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} {A92B4734-2587-4F6F-97A3-741BE48709A5} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} {28F8E9E2-FE24-41F7-A888-9FC244A9E2DD} = {4082AA58-D410-4C52-8E88-3B0A4E860B28} + {9A3DEA7E-1681-4D48-AC5C-1F0DE421A203} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A} + {7FD0A92B-7862-4BB1-932B-B52A9CACB56B} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} EndGlobalSection EndGlobal diff --git a/src/PinkParrot.Events/Apps/AppCreated.cs b/src/PinkParrot.Events/Apps/AppCreated.cs new file mode 100644 index 000000000..edd6b3bff --- /dev/null +++ b/src/PinkParrot.Events/Apps/AppCreated.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// AppCreated.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using PinkParrot.Infrastructure.CQRS.Events; + +namespace PinkParrot.Events.Apps +{ + public class AppCreated : IEvent + { + public string Name { get; set; } + } +} diff --git a/src/PinkParrot.Infrastructure/Guard.cs b/src/PinkParrot.Infrastructure/Guard.cs index b5aefcda7..08c613b0d 100644 --- a/src/PinkParrot.Infrastructure/Guard.cs +++ b/src/PinkParrot.Infrastructure/Guard.cs @@ -196,5 +196,14 @@ namespace PinkParrot.Infrastructure throw new ArgumentException("Value contains an invalid character.", parameterName); } } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Valid(IValidatable target, string parameterName, string message) + { + NotNull(target, parameterName); + + target.Validate(() => message); + } } } diff --git a/src/PinkParrot.Store.MongoDb/Infrastructure/MongoPosition.cs b/src/PinkParrot.Store.MongoDb/Infrastructure/MongoPositionEntity.cs similarity index 100% rename from src/PinkParrot.Store.MongoDb/Infrastructure/MongoPosition.cs rename to src/PinkParrot.Store.MongoDb/Infrastructure/MongoPositionEntity.cs diff --git a/src/PinkParrot.Store.MongoDb/MongoDbModule.cs b/src/PinkParrot.Store.MongoDb/MongoDbModule.cs new file mode 100644 index 000000000..24ac249c0 --- /dev/null +++ b/src/PinkParrot.Store.MongoDb/MongoDbModule.cs @@ -0,0 +1,57 @@ +// ========================================================================== +// MongoDbModule.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using Autofac; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.MongoDB; +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using PinkParrot.Infrastructure.CQRS.EventStore; +using PinkParrot.Store.MongoDb.Infrastructure; + +namespace PinkParrot.Store.MongoDb +{ + public class MongoDbModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.Register(context => + { + var options = context.Resolve>().Value; + + var mongoDbClient = new MongoClient(options.ConnectionString); + var mongoDatabase = mongoDbClient.GetDatabase(options.DatabaseName); + + return mongoDatabase; + }).SingleInstance(); + + builder.Register>(context => + { + var usersCollection = context.Resolve().GetCollection("Identity_Users"); + + IndexChecks.EnsureUniqueIndexOnNormalizedEmail(usersCollection); + IndexChecks.EnsureUniqueIndexOnNormalizedUserName(usersCollection); + + return new UserStore(usersCollection); + }).SingleInstance(); + + builder.Register>(context => + { + var rolesCollection = context.Resolve().GetCollection("Identity_Roles"); + + IndexChecks.EnsureUniqueIndexOnNormalizedRoleName(rolesCollection); + + return new RoleStore(rolesCollection); + }).SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance(); + } + } +} diff --git a/src/PinkParrot.Store.MongoDb/MongoDbOptions.cs b/src/PinkParrot.Store.MongoDb/MongoDbOptions.cs new file mode 100644 index 000000000..56ccbba5f --- /dev/null +++ b/src/PinkParrot.Store.MongoDb/MongoDbOptions.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// MongoDbOptions.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +namespace PinkParrot.Store.MongoDb +{ + public class MongoDbOptions + { + public string ConnectionString { get; set; } + + public string DatabaseName { get; set; } + } +} diff --git a/src/PinkParrot.Store.MongoDb/project.json b/src/PinkParrot.Store.MongoDb/project.json index f4e796622..58b38c279 100644 --- a/src/PinkParrot.Store.MongoDb/project.json +++ b/src/PinkParrot.Store.MongoDb/project.json @@ -1,5 +1,8 @@ { "dependencies": { + "Autofac": "4.1.1", + "Microsoft.AspNetCore.Identity": "1.0.0", + "Microsoft.AspNetCore.Identity.MongoDB": "1.0.2", "MongoDB.Driver": "2.4.0-beta1", "NETStandard.Library": "1.6.0", "PinkParrot.Core": "1.0.0-*", diff --git a/src/PinkParrot.Write/Apps/AppDomainObject.cs b/src/PinkParrot.Write/Apps/AppDomainObject.cs new file mode 100644 index 000000000..49ecd05c1 --- /dev/null +++ b/src/PinkParrot.Write/Apps/AppDomainObject.cs @@ -0,0 +1,59 @@ +// ========================================================================== +// AppDomainObject.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using PinkParrot.Events.Apps; +using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS; +using PinkParrot.Infrastructure.CQRS.Events; +using PinkParrot.Infrastructure.Dispatching; +using PinkParrot.Write.Apps.Commands; + +namespace PinkParrot.Write.Apps +{ + public sealed class AppDomainObject : DomainObject + { + private string name; + + public string Name + { + get { return name; } + } + + public AppDomainObject(Guid id, int version) : base(id, version) + { + } + + public void On(AppCreated @event) + { + name = @event.Name; + } + + protected override void DispatchEvent(Envelope @event) + { + this.DispatchAction(@event.Payload); + } + + public void Create(CreateApp command) + { + Guard.Valid(command, nameof(command), "Cannot create app"); + + VerifyNotCreated(); + + RaiseEvent(new AppCreated { Name = command.Name }); + } + + private void VerifyNotCreated() + { + if (!string.IsNullOrWhiteSpace(name)) + { + throw new DomainException("App has already been created."); + } + } + } +} diff --git a/src/PinkParrot.Write/Apps/Commands/CreateApp.cs b/src/PinkParrot.Write/Apps/Commands/CreateApp.cs new file mode 100644 index 000000000..162253f8e --- /dev/null +++ b/src/PinkParrot.Write/Apps/Commands/CreateApp.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// CreateApp.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using PinkParrot.Infrastructure; + +namespace PinkParrot.Write.Apps.Commands +{ + public sealed class CreateApp : IValidatable + { + public string Name { get; set; } + + public void Validate(IList errors) + { + if (!Name.IsSlug()) + { + errors.Add(new ValidationError("Name must be a valid slug", "Name")); + } + } + } +} \ No newline at end of file diff --git a/src/PinkParrot/Configurations/EventStoreModule.cs b/src/PinkParrot/Configurations/EventStoreModule.cs new file mode 100644 index 000000000..1dd331540 --- /dev/null +++ b/src/PinkParrot/Configurations/EventStoreModule.cs @@ -0,0 +1,55 @@ +// ========================================================================== +// EventStoreModule.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Net; +using Autofac; +using EventStore.ClientAPI; +using EventStore.ClientAPI.SystemData; +using Microsoft.Extensions.Options; +using PinkParrot.Infrastructure.CQRS.EventStore; + +namespace PinkParrot.Configurations +{ + public class EventStoreModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.Register(context => + { + var options = context.Resolve>().Value; + + var eventStore = + EventStoreConnection.Create( + ConnectionSettings.Create() + .UseConsoleLogger() + .UseDebugLogger() + .KeepReconnecting() + .KeepRetrying(), + new IPEndPoint(IPAddress.Parse(options.IPAddress), options.Port)); + + eventStore.ConnectAsync().Wait(); + + return eventStore; + }).SingleInstance(); + + builder.Register(context => + { + var options = context.Resolve>().Value; + + return new UserCredentials(options.Username, options.Password); + }).SingleInstance(); + + builder.Register(context => + { + var options = context.Resolve>().Value; + + return new DefaultNameResolver(options.Prefix); + }).SingleInstance(); + } + } +} diff --git a/src/PinkParrot/Configurations/EventStoreOptions.cs b/src/PinkParrot/Configurations/EventStoreOptions.cs new file mode 100644 index 000000000..96b119484 --- /dev/null +++ b/src/PinkParrot/Configurations/EventStoreOptions.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// EventStoreOptions.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +namespace PinkParrot.Configurations +{ + public sealed class EventStoreOptions + { + public string IPAddress { get; set; } + + public string Username { get; set; } + + public string Password { get; set; } + + public string Prefix { get; set; } + + public int Port { get; set; } + } +} diff --git a/src/PinkParrot/Configurations/EventStoreUsage.cs b/src/PinkParrot/Configurations/EventStoreUsage.cs new file mode 100644 index 000000000..21123e84a --- /dev/null +++ b/src/PinkParrot/Configurations/EventStoreUsage.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// EventStoreUsage.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using PinkParrot.Infrastructure.CQRS.EventStore; + +namespace PinkParrot.Configurations +{ + public static class EventStoreUsage + { + public static void UseEventStore(this IApplicationBuilder app) + { + var options = app.ApplicationServices.GetService>().Value; + + app.ApplicationServices.GetService().Subscribe(options.Prefix); + } + } +} diff --git a/src/PinkParrot/Configurations/IdentityOptions.cs b/src/PinkParrot/Configurations/IdentityOptions.cs new file mode 100644 index 000000000..45a2a2e22 --- /dev/null +++ b/src/PinkParrot/Configurations/IdentityOptions.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// IdentityOptions.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +namespace PinkParrot.Configurations +{ + public sealed class IdentityOptions + { + public string DefaultUsername { get; set; } + + public string DefaultPassword { get; set; } + } +} diff --git a/src/PinkParrot/Configurations/IdentityUsage.cs b/src/PinkParrot/Configurations/IdentityUsage.cs new file mode 100644 index 000000000..229f47d7c --- /dev/null +++ b/src/PinkParrot/Configurations/IdentityUsage.cs @@ -0,0 +1,43 @@ +// ========================================================================== +// IdentityUsage.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.MongoDB; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace PinkParrot.Configurations +{ + public static class IdentityUsage + { + public static void UseDefaultUser(this IApplicationBuilder app) + { + var options = app.ApplicationServices.GetService>().Value; + + var username = options.DefaultUsername; + var userManager = app.ApplicationServices.GetService>(); + + if (!string.IsNullOrWhiteSpace(options.DefaultUsername) && + !string.IsNullOrWhiteSpace(options.DefaultPassword)) + { + Task.Run(async () => + { + if (userManager.SupportsQueryableUsers && !userManager.Users.Any()) + { + var user = new IdentityUser { UserName = username, Email = username, EmailConfirmed = true }; + + await userManager.CreateAsync(user, options.DefaultPassword); + } + }).Wait(); + } + } + } +} diff --git a/src/PinkParrot/Configurations/InfrastructureModule.cs b/src/PinkParrot/Configurations/InfrastructureModule.cs index c93c1b7b1..b403492aa 100644 --- a/src/PinkParrot/Configurations/InfrastructureModule.cs +++ b/src/PinkParrot/Configurations/InfrastructureModule.cs @@ -6,20 +6,13 @@ // All rights reserved. // ========================================================================== -using System.Net; using Autofac; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Driver; using PinkParrot.Core.Schemas; using PinkParrot.Infrastructure.CQRS.Autofac; using PinkParrot.Infrastructure.CQRS.Commands; using PinkParrot.Infrastructure.CQRS.EventStore; -using PinkParrot.Pipeline; using PinkParrot.Store.MongoDb.Infrastructure; namespace PinkParrot.Configurations @@ -28,24 +21,6 @@ namespace PinkParrot.Configurations { protected override void Load(ContainerBuilder builder) { - var eventStore = - EventStoreConnection.Create( - ConnectionSettings.Create() - .UseConsoleLogger() - .UseDebugLogger() - .KeepReconnecting() - .KeepRetrying(), - new IPEndPoint(IPAddress.Loopback, 1113)); - - var mongoDbClient = new MongoClient("mongodb://localhost"); - var mongoDatabase = mongoDbClient.GetDatabase("PinkParrot"); - - eventStore.ConnectAsync().Wait(); - - builder.RegisterInstance(new UserCredentials("admin", "changeit")) - .AsSelf() - .SingleInstance(); - builder.RegisterType() .As() .SingleInstance(); @@ -54,10 +29,6 @@ namespace PinkParrot.Configurations .As() .SingleInstance(); - builder.RegisterInstance(mongoDatabase) - .As() - .SingleInstance(); - builder.RegisterType() .As() .SingleInstance(); @@ -66,14 +37,6 @@ namespace PinkParrot.Configurations .As() .SingleInstance(); - builder.RegisterInstance(new DefaultNameResolver("pinkparrot")) - .As() - .SingleInstance(); - - builder.RegisterInstance(eventStore) - .As() - .SingleInstance(); - builder.RegisterType() .As() .SingleInstance(); @@ -91,17 +54,4 @@ namespace PinkParrot.Configurations .SingleInstance(); } } - - public static class InfrastructureDependencie - { - public static void UseAppEventBus(this IApplicationBuilder app) - { - app.ApplicationServices.GetService().Subscribe("pinkparrot"); - } - - public static void UseAppTenants(this IApplicationBuilder app) - { - app.UseMiddleware(); - } - } } diff --git a/src/PinkParrot/Configurations/InfrastructureUsage.cs b/src/PinkParrot/Configurations/InfrastructureUsage.cs new file mode 100644 index 000000000..22fa27146 --- /dev/null +++ b/src/PinkParrot/Configurations/InfrastructureUsage.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// InfrastructureUsage.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Builder; +using PinkParrot.Pipeline; + +namespace PinkParrot.Configurations +{ + public static class InfrastructureUsage + { + public static void UseTenants(this IApplicationBuilder app) + { + app.UseMiddleware(); + } + } +} diff --git a/src/PinkParrot/Startup.cs b/src/PinkParrot/Startup.cs index 2a5a843bd..43db88f30 100644 --- a/src/PinkParrot/Startup.cs +++ b/src/PinkParrot/Startup.cs @@ -11,9 +11,12 @@ using Autofac; using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity.MongoDB; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PinkParrot.Configurations; +using PinkParrot.Store.MongoDb; // ReSharper disable AccessToModifiedClosure @@ -21,12 +24,32 @@ namespace PinkParrot { public class Startup { + public IConfigurationRoot Configuration { get; } + + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true) + .AddEnvironmentVariables(); + + Configuration = builder.Build(); + } + public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc().AddAppSerializers(); services.AddRouting(); services.AddMemoryCache(); + services.AddOptions(); services.AddEventFormatter(); + services.AddIdentity().AddDefaultTokenProviders(); + + services.Configure( + Configuration.GetSection("MongoDb")); + services.Configure( + Configuration.GetSection("EventStore")); var builder = new ContainerBuilder(); builder.RegisterModule(); @@ -46,10 +69,11 @@ namespace PinkParrot app.UseDeveloperExceptionPage(); } - app.UseAppTenants(); + app.UseTenants(); app.UseMvc(); app.UseStaticFiles(); - app.UseAppEventBus(); + app.UseEventStore(); + app.UseDefaultUser(); } } } diff --git a/src/PinkParrot/appsettings.json b/src/PinkParrot/appsettings.json new file mode 100644 index 000000000..a9348c710 --- /dev/null +++ b/src/PinkParrot/appsettings.json @@ -0,0 +1,13 @@ +{ + "MongoDb": { + "ConnectionString": "mongodb://localhost", + "DatabaseName": "PinkParrot" + }, + "EventStore": { + "IPAddress": "127.0.0.1", + "Port": 1113, + "Prefix": "pinkparrot", + "Username": "admin", + "Password": "changeit" + } +} \ No newline at end of file diff --git a/src/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs b/tests/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs rename to tests/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs diff --git a/src/PinkParrot.Infrastructure.Tests/DispatchingTests.cs b/tests/PinkParrot.Infrastructure.Tests/DispatchingTests.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/DispatchingTests.cs rename to tests/PinkParrot.Infrastructure.Tests/DispatchingTests.cs diff --git a/src/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs b/tests/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs rename to tests/PinkParrot.Infrastructure.Tests/EnumExtensionsTests.cs diff --git a/src/PinkParrot.Infrastructure.Tests/GuardTests.cs b/tests/PinkParrot.Infrastructure.Tests/GuardTests.cs similarity index 90% rename from src/PinkParrot.Infrastructure.Tests/GuardTests.cs rename to tests/PinkParrot.Infrastructure.Tests/GuardTests.cs index 763545628..fba187198 100644 --- a/src/PinkParrot.Infrastructure.Tests/GuardTests.cs +++ b/tests/PinkParrot.Infrastructure.Tests/GuardTests.cs @@ -7,10 +7,26 @@ // ========================================================================== using System; +using System.Collections.Generic; using Xunit; namespace PinkParrot.Infrastructure { + public sealed class ValidatableValid : IValidatable + { + public void Validate(IList errors) + { + } + } + + public sealed class ValidatableInvalid : IValidatable + { + public void Validate(IList errors) + { + errors.Add(new ValidationError("error", "error")); + } + } + public class GuardTest { [Theory] @@ -301,5 +317,23 @@ namespace PinkParrot.Infrastructure { Guard.ValidFileName("FileName", "Parameter"); } + + [Fact] + public void Valid_should_throw_if_null() + { + Assert.Throws(() => Guard.Valid(null, "Parameter", "Message")); + } + + [Fact] + public void Valid_should_throw_if_invalid() + { + Assert.Throws(() => Guard.Valid(new ValidatableInvalid(), "Parameter", "Message")); + } + + [Fact] + public void Valid_should_do_nothing_if_valid() + { + Guard.Valid(new ValidatableValid(), "Parameter", "Message"); + } } } \ No newline at end of file diff --git a/src/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj b/tests/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj similarity index 94% rename from src/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj rename to tests/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj index 14f6cb7a2..07eb002e9 100644 --- a/src/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj +++ b/tests/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj @@ -6,7 +6,7 @@ - 840c02b1-48f8-4c8a-8862-8a3fdefde8d5 + 7fd0a92b-7862-4bb1-932b-b52a9cacb56b PinkParrot.Infrastructure .\obj .\bin\ diff --git a/src/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs b/tests/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs rename to tests/PinkParrot.Infrastructure.Tests/PropertiesBagTests.cs diff --git a/src/PinkParrot.Infrastructure.Tests/Reflection/PropertiesTypeAccessorTest.cs b/tests/PinkParrot.Infrastructure.Tests/Reflection/PropertiesTypeAccessorTest.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/Reflection/PropertiesTypeAccessorTest.cs rename to tests/PinkParrot.Infrastructure.Tests/Reflection/PropertiesTypeAccessorTest.cs diff --git a/src/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs b/tests/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs rename to tests/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs diff --git a/src/PinkParrot.Infrastructure.Tests/TypeNameRegistryTests.cs b/tests/PinkParrot.Infrastructure.Tests/TypeNameRegistryTests.cs similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/TypeNameRegistryTests.cs rename to tests/PinkParrot.Infrastructure.Tests/TypeNameRegistryTests.cs diff --git a/src/PinkParrot.Infrastructure.Tests/project.json b/tests/PinkParrot.Infrastructure.Tests/project.json similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/project.json rename to tests/PinkParrot.Infrastructure.Tests/project.json diff --git a/src/PinkParrot.Infrastructure.Tests/xunit.runner.json b/tests/PinkParrot.Infrastructure.Tests/xunit.runner.json similarity index 100% rename from src/PinkParrot.Infrastructure.Tests/xunit.runner.json rename to tests/PinkParrot.Infrastructure.Tests/xunit.runner.json diff --git a/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs b/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs new file mode 100644 index 000000000..36fd9eaf2 --- /dev/null +++ b/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using PinkParrot.Infrastructure; +using PinkParrot.Write.Apps; +using PinkParrot.Write.Apps.Commands; +using Xunit; + +namespace PinkParrot.Write.Tests.Apps +{ + public class AppDomainObjectTest + { + private readonly AppDomainObject sut = new AppDomainObject(Guid.NewGuid(), 0); + + [Fact] + public void Create_should_throw_if_created() + { + sut.Create(new CreateApp { Name = "app" }); + + Assert.Throws(() => sut.Create(new CreateApp { Name = "app" })); + } + + [Fact] + public void Create_should_specify_name() + { + sut.Create(new CreateApp { Name = "app" }); + + Assert.Equal("app", sut.Name); + } + } +} diff --git a/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj b/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj new file mode 100644 index 000000000..d598820f2 --- /dev/null +++ b/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 9a3dea7e-1681-4d48-ac5c-1f0de421a203 + PinkParrot.Write.Tests + .\obj + .\bin\ + v4.6.1 + + + 2.0 + + + \ No newline at end of file diff --git a/tests/PinkParrot.Write.Tests/Properties/AssemblyInfo.cs b/tests/PinkParrot.Write.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1b84cf89d --- /dev/null +++ b/tests/PinkParrot.Write.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PinkParrot.Write.Tests")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6b9364c6-94a1-4631-9cf1-87616afaf1ea")] diff --git a/tests/PinkParrot.Write.Tests/project.json b/tests/PinkParrot.Write.Tests/project.json new file mode 100644 index 000000000..5a74dc793 --- /dev/null +++ b/tests/PinkParrot.Write.Tests/project.json @@ -0,0 +1,31 @@ +{ + "buildOptions": { + "copyToOutput": { + "include": [ + "xunit.runner.json" + ] + } + }, + "dependencies": { + "dotnet-test-xunit": "2.2.0-preview2-build1029", + "PinkParrot.Core": "1.0.0-*", + "PinkParrot.Infrastructure": "1.0.0-*", + "PinkParrot.Write": "1.0.0-*", + "xunit": "2.2.0-beta3-build3402" + }, + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.1" + } + } + } + }, + "testRunner": "xunit", + "tooling": { + "defaultNamespace": "PinkParrot.Core.Tests" + }, + "version": "1.0.0-*" +} \ No newline at end of file