diff --git a/src/PinkParrot.Events/Schemas/FieldAdded.cs b/src/PinkParrot.Events/Schemas/FieldAdded.cs index a2b2526b8..1249945ee 100644 --- a/src/PinkParrot.Events/Schemas/FieldAdded.cs +++ b/src/PinkParrot.Events/Schemas/FieldAdded.cs @@ -8,11 +8,12 @@ using PinkParrot.Core.Schemas; using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldAddedEvent")] - public class FieldAdded : AppEvent + public class FieldAdded : IEvent { public long FieldId { get; set; } diff --git a/src/PinkParrot.Events/Schemas/FieldDeleted.cs b/src/PinkParrot.Events/Schemas/FieldDeleted.cs index a0c15bcd8..0e52412aa 100644 --- a/src/PinkParrot.Events/Schemas/FieldDeleted.cs +++ b/src/PinkParrot.Events/Schemas/FieldDeleted.cs @@ -7,11 +7,12 @@ // ========================================================================== using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldDeletedEvent")] - public class FieldDeleted : AppEvent + public class FieldDeleted : IEvent { public long FieldId { get; set; } } diff --git a/src/PinkParrot.Events/Schemas/FieldDisabled.cs b/src/PinkParrot.Events/Schemas/FieldDisabled.cs index 13959641a..c5e9056c2 100644 --- a/src/PinkParrot.Events/Schemas/FieldDisabled.cs +++ b/src/PinkParrot.Events/Schemas/FieldDisabled.cs @@ -7,11 +7,12 @@ // ========================================================================== using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldDisabledEvent")] - public class FieldDisabled : AppEvent + public class FieldDisabled : IEvent { public long FieldId { get; set; } } diff --git a/src/PinkParrot.Events/Schemas/FieldEnabled.cs b/src/PinkParrot.Events/Schemas/FieldEnabled.cs index aa3062829..6716f54f8 100644 --- a/src/PinkParrot.Events/Schemas/FieldEnabled.cs +++ b/src/PinkParrot.Events/Schemas/FieldEnabled.cs @@ -7,11 +7,12 @@ // ========================================================================== using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldEnabledEvent")] - public class FieldEnabled : AppEvent + public class FieldEnabled : IEvent { public long FieldId { get; set; } } diff --git a/src/PinkParrot.Events/Schemas/FieldHidden.cs b/src/PinkParrot.Events/Schemas/FieldHidden.cs index d24fd6c8f..a2874b871 100644 --- a/src/PinkParrot.Events/Schemas/FieldHidden.cs +++ b/src/PinkParrot.Events/Schemas/FieldHidden.cs @@ -7,11 +7,12 @@ // ========================================================================== using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldHiddenEvent")] - public class FieldHidden : AppEvent + public class FieldHidden : IEvent { public long FieldId { get; set; } } diff --git a/src/PinkParrot.Events/Schemas/FieldShown.cs b/src/PinkParrot.Events/Schemas/FieldShown.cs index 00ef75e65..aa737fac0 100644 --- a/src/PinkParrot.Events/Schemas/FieldShown.cs +++ b/src/PinkParrot.Events/Schemas/FieldShown.cs @@ -7,11 +7,12 @@ // ========================================================================== using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldShownEvent")] - public class FieldShown : AppEvent + public class FieldShown : IEvent { public long FieldId { get; set; } } diff --git a/src/PinkParrot.Events/Schemas/FieldUpdated.cs b/src/PinkParrot.Events/Schemas/FieldUpdated.cs index 594e517e8..61a8def95 100644 --- a/src/PinkParrot.Events/Schemas/FieldUpdated.cs +++ b/src/PinkParrot.Events/Schemas/FieldUpdated.cs @@ -8,11 +8,12 @@ using PinkParrot.Core.Schemas; using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("FieldUpdatedEvent")] - public class FieldUpdated : AppEvent + public class FieldUpdated : IEvent { public long FieldId { get; set; } diff --git a/src/PinkParrot.Events/Schemas/SchemaDeleted.cs b/src/PinkParrot.Events/Schemas/SchemaDeleted.cs index 0ed6e76be..25576db9a 100644 --- a/src/PinkParrot.Events/Schemas/SchemaDeleted.cs +++ b/src/PinkParrot.Events/Schemas/SchemaDeleted.cs @@ -7,11 +7,12 @@ // ========================================================================== using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("SchemaDeleted")] - public class SchemaDeleted : AppEvent + public class SchemaDeleted : IEvent { } } diff --git a/src/PinkParrot.Events/Schemas/SchemaUpdated.cs b/src/PinkParrot.Events/Schemas/SchemaUpdated.cs index 9f61651a9..ce5565399 100644 --- a/src/PinkParrot.Events/Schemas/SchemaUpdated.cs +++ b/src/PinkParrot.Events/Schemas/SchemaUpdated.cs @@ -8,11 +8,12 @@ using PinkParrot.Core.Schemas; using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Events; namespace PinkParrot.Events.Schemas { [TypeName("SchemaUpdated")] - public class SchemaUpdated : AppEvent + public class SchemaUpdated : IEvent { public SchemaProperties Properties { get; set; } } diff --git a/src/PinkParrot.Infrastructure/CQRS/DomainObject.cs b/src/PinkParrot.Infrastructure/CQRS/DomainObject.cs index 14c34ebe0..8bedaae69 100644 --- a/src/PinkParrot.Infrastructure/CQRS/DomainObject.cs +++ b/src/PinkParrot.Infrastructure/CQRS/DomainObject.cs @@ -71,7 +71,7 @@ namespace PinkParrot.Infrastructure.CQRS uncomittedEvents.Clear(); } - ICollection> IAggregate.GetUncomittedEvents() + public ICollection> GetUncomittedEvents() { return uncomittedEvents; } diff --git a/src/PinkParrot.Infrastructure/Guard.cs b/src/PinkParrot.Infrastructure/Guard.cs index 08c613b0d..9cbedf40c 100644 --- a/src/PinkParrot.Infrastructure/Guard.cs +++ b/src/PinkParrot.Infrastructure/Guard.cs @@ -199,11 +199,11 @@ namespace PinkParrot.Infrastructure [DebuggerStepThrough] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Valid(IValidatable target, string parameterName, string message) + public static void Valid(IValidatable target, string parameterName, Func message) { NotNull(target, parameterName); - target.Validate(() => message); + target.Validate(message); } } } diff --git a/src/PinkParrot.Infrastructure/TypeNameRegistry.cs b/src/PinkParrot.Infrastructure/TypeNameRegistry.cs index 928ebb50f..a8f1f8f4a 100644 --- a/src/PinkParrot.Infrastructure/TypeNameRegistry.cs +++ b/src/PinkParrot.Infrastructure/TypeNameRegistry.cs @@ -72,7 +72,7 @@ namespace PinkParrot.Infrastructure if (result == null) { - throw new ArgumentException($"There is not name for type '{type}"); + throw new ArgumentException($"There is no name for type '{type}"); } return result; @@ -84,7 +84,7 @@ namespace PinkParrot.Infrastructure if (result == null) { - throw new ArgumentException($"There is not type for name '{name}"); + throw new ArgumentException($"There is no type for name '{name}"); } return result; diff --git a/src/PinkParrot.Read/Apps/Repositories/IAppRepository.cs b/src/PinkParrot.Read/Apps/Repositories/IAppRepository.cs index 02c0f295b..7809c8818 100644 --- a/src/PinkParrot.Read/Apps/Repositories/IAppRepository.cs +++ b/src/PinkParrot.Read/Apps/Repositories/IAppRepository.cs @@ -6,12 +6,15 @@ // All rights reserved. // ========================================================================== +using System.Collections.Generic; using System.Threading.Tasks; namespace PinkParrot.Read.Apps.Repositories { public interface IAppRepository { + Task> QueryAllAsync(); + Task FindAppByNameAsync(string name); } } diff --git a/src/PinkParrot.Read/Schemas/Repositories/ISchemaRepository.cs b/src/PinkParrot.Read/Schemas/Repositories/ISchemaRepository.cs index 647fa800f..fc6e480f5 100644 --- a/src/PinkParrot.Read/Schemas/Repositories/ISchemaRepository.cs +++ b/src/PinkParrot.Read/Schemas/Repositories/ISchemaRepository.cs @@ -14,7 +14,7 @@ namespace PinkParrot.Read.Schemas.Repositories { public interface ISchemaRepository { - Task> QueryAllAsync(Guid appId); + Task> QueryAllAsync(Guid appId); Task FindSchemaIdAsync(Guid appId, string name); diff --git a/src/PinkParrot.Store.MongoDb/Apps/MongoAppEntity.cs b/src/PinkParrot.Store.MongoDb/Apps/MongoAppEntity.cs new file mode 100644 index 000000000..a86f0fe00 --- /dev/null +++ b/src/PinkParrot.Store.MongoDb/Apps/MongoAppEntity.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// MongoAppEntity.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using MongoDB.Bson.Serialization.Attributes; +using PinkParrot.Read.Apps; +using PinkParrot.Store.MongoDb.Utils; + +namespace PinkParrot.Store.MongoDb.Apps +{ + public sealed class MongoAppEntity : MongoEntity, IAppEntity + { + [BsonRequired] + [BsonElement] + public string Name { get; set; } + } +} diff --git a/src/PinkParrot.Store.MongoDb/Apps/MongoAppRepository.cs b/src/PinkParrot.Store.MongoDb/Apps/MongoAppRepository.cs new file mode 100644 index 000000000..bd18a170c --- /dev/null +++ b/src/PinkParrot.Store.MongoDb/Apps/MongoAppRepository.cs @@ -0,0 +1,57 @@ +// ========================================================================== +// MongoAppRepository.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver; +using PinkParrot.Events.Apps; +using PinkParrot.Infrastructure.CQRS; +using PinkParrot.Infrastructure.CQRS.Events; +using PinkParrot.Infrastructure.Dispatching; +using PinkParrot.Infrastructure.Reflection; +using PinkParrot.Read.Apps; +using PinkParrot.Read.Apps.Repositories; +using PinkParrot.Store.MongoDb.Utils; + +namespace PinkParrot.Store.MongoDb.Apps +{ + public sealed class MongoAppRepository : MongoRepositoryBase, IAppRepository, ICatchEventConsumer + { + public MongoAppRepository(IMongoDatabase database) + : base(database) + { + } + + public async Task> QueryAllAsync() + { + var entities = + await Collection.Find(new BsonDocument()).ToListAsync(); + + return entities; + } + + public async Task FindAppByNameAsync(string name) + { + var entity = + await Collection.Find(s => s.Name == name).FirstOrDefaultAsync(); + + return entity; + } + + public Task On(AppCreated @event, EnvelopeHeaders headers) + { + return Collection.CreateAsync(headers, a => SimpleMapper.Map(a, @event)); + } + + public Task On(Envelope @event) + { + return this.DispatchActionAsync(@event.Payload, @event.Headers); + } + } +} diff --git a/src/PinkParrot.Store.MongoDb/MongoDbModule.cs b/src/PinkParrot.Store.MongoDb/MongoDbModule.cs index 24ac249c0..e50b00757 100644 --- a/src/PinkParrot.Store.MongoDb/MongoDbModule.cs +++ b/src/PinkParrot.Store.MongoDb/MongoDbModule.cs @@ -12,7 +12,11 @@ using Microsoft.AspNetCore.Identity.MongoDB; using Microsoft.Extensions.Options; using MongoDB.Driver; using PinkParrot.Infrastructure.CQRS.EventStore; +using PinkParrot.Read.Apps.Repositories; +using PinkParrot.Read.Schemas.Repositories; +using PinkParrot.Store.MongoDb.Apps; using PinkParrot.Store.MongoDb.Infrastructure; +using PinkParrot.Store.MongoDb.Schemas; namespace PinkParrot.Store.MongoDb { @@ -52,6 +56,14 @@ namespace PinkParrot.Store.MongoDb builder.RegisterType() .As() .SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance(); } } } diff --git a/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaEntity.cs b/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaEntity.cs index c9d321c15..af40b9361 100644 --- a/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaEntity.cs +++ b/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaEntity.cs @@ -17,27 +17,14 @@ using PinkParrot.Store.MongoDb.Utils; namespace PinkParrot.Store.MongoDb.Schemas { - public sealed class MongoSchemaEntity : ISchemaEntityWithSchema + public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntityWithSchema { private Schema schema; - [BsonId] - [BsonElement] - [BsonRepresentation(BsonType.String)] - public Guid Id { get; set; } - [BsonRequired] [BsonElement] public string Name { get; set; } - [BsonRequired] - [BsonElement] - public DateTime Created { get; set; } - - [BsonRequired] - [BsonElement] - public DateTime LastModified { get; set; } - [BsonRequired] [BsonElement] public Guid AppId { get; set; } diff --git a/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaRepository.cs b/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaRepository.cs index 2784906dc..1fa031ecc 100644 --- a/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/PinkParrot.Store.MongoDb/Schemas/MongoSchemaRepository.cs @@ -18,6 +18,7 @@ using PinkParrot.Infrastructure; using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS.Events; using PinkParrot.Infrastructure.Dispatching; +using PinkParrot.Infrastructure.Reflection; using PinkParrot.Read.Schemas.Repositories; using PinkParrot.Store.MongoDb.Schemas.Models; using PinkParrot.Store.MongoDb.Utils; @@ -44,7 +45,7 @@ namespace PinkParrot.Store.MongoDb.Schemas return collection.Indexes.CreateOneAsync(IndexKeys.Ascending(x => x.Name)); } - public async Task> QueryAllAsync(Guid appId) + public async Task> QueryAllAsync(Guid appId) { var entities = await Collection.Find(s => s.AppId == appId && !s.IsDeleted).ToListAsync(); @@ -131,12 +132,7 @@ namespace PinkParrot.Store.MongoDb.Schemas public Task On(SchemaCreated @event, EnvelopeHeaders headers) { - return Collection.CreateAsync(headers, e => - { - e.Name = @event.Name; - - Serialize(e, Schema.Create(@event.Name, @event.Properties)); - }); + return Collection.CreateAsync(headers, s => SimpleMapper.Map(s, @event)); } public Task On(Envelope @event) diff --git a/src/PinkParrot.Store.MongoDb/Utils/MongoEntity.cs b/src/PinkParrot.Store.MongoDb/Utils/MongoEntity.cs new file mode 100644 index 000000000..a712554e3 --- /dev/null +++ b/src/PinkParrot.Store.MongoDb/Utils/MongoEntity.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// MongoEntity.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using PinkParrot.Read; + +namespace PinkParrot.Store.MongoDb.Utils +{ + public abstract class MongoEntity : IEntity + { + [BsonId] + [BsonElement] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + [BsonRequired] + [BsonElement] + public DateTime Created { get; set; } + + [BsonRequired] + [BsonElement] + public DateTime LastModified { get; set; } + } +} diff --git a/src/PinkParrot.Write/Apps/AppCommandHandler.cs b/src/PinkParrot.Write/Apps/AppCommandHandler.cs new file mode 100644 index 000000000..bbeae2761 --- /dev/null +++ b/src/PinkParrot.Write/Apps/AppCommandHandler.cs @@ -0,0 +1,35 @@ +// ========================================================================== +// AppCommandHandler.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System.Threading.Tasks; +using PinkParrot.Infrastructure.CQRS.Commands; +using PinkParrot.Infrastructure.Dispatching; +using PinkParrot.Write.Apps.Commands; + +namespace PinkParrot.Write.Apps +{ + public class AppCommandHandler : CommandHandler + { + public AppCommandHandler( + IDomainObjectFactory domainObjectFactory, + IDomainObjectRepository domainObjectRepository) + : base(domainObjectFactory, domainObjectRepository) + { + } + + public Task On(CreateApp command) + { + return CreateAsync(command, x => x.Create(command)); + } + + public override Task HandleAsync(CommandContext context) + { + return context.IsHandled ? Task.FromResult(false) : this.DispatchActionAsync(context.Command); + } + } +} diff --git a/src/PinkParrot.Write/Apps/AppDomainObject.cs b/src/PinkParrot.Write/Apps/AppDomainObject.cs index 49ecd05c1..74ace264c 100644 --- a/src/PinkParrot.Write/Apps/AppDomainObject.cs +++ b/src/PinkParrot.Write/Apps/AppDomainObject.cs @@ -41,7 +41,7 @@ namespace PinkParrot.Write.Apps public void Create(CreateApp command) { - Guard.Valid(command, nameof(command), "Cannot create app"); + Guard.Valid(command, nameof(command), () => "Cannot create app"); VerifyNotCreated(); diff --git a/src/PinkParrot.Write/Apps/Commands/CreateApp.cs b/src/PinkParrot.Write/Apps/Commands/CreateApp.cs index 162253f8e..e711bb7e2 100644 --- a/src/PinkParrot.Write/Apps/Commands/CreateApp.cs +++ b/src/PinkParrot.Write/Apps/Commands/CreateApp.cs @@ -8,10 +8,11 @@ using System.Collections.Generic; using PinkParrot.Infrastructure; +using PinkParrot.Infrastructure.CQRS.Commands; namespace PinkParrot.Write.Apps.Commands { - public sealed class CreateApp : IValidatable + public sealed class CreateApp : AggregateCommand, IValidatable { public string Name { get; set; } @@ -19,7 +20,7 @@ namespace PinkParrot.Write.Apps.Commands { if (!Name.IsSlug()) { - errors.Add(new ValidationError("Name must be a valid slug", "Name")); + errors.Add(new ValidationError("Name must be a valid slug", nameof(Name))); } } } diff --git a/src/PinkParrot.Write/Schemas/Commands/AddField.cs b/src/PinkParrot.Write/Schemas/Commands/AddField.cs index 49c056fc5..945943ceb 100644 --- a/src/PinkParrot.Write/Schemas/Commands/AddField.cs +++ b/src/PinkParrot.Write/Schemas/Commands/AddField.cs @@ -6,16 +6,36 @@ // All rights reserved. // ========================================================================== +using System.Collections.Generic; using Newtonsoft.Json.Linq; +using PinkParrot.Infrastructure; namespace PinkParrot.Write.Schemas.Commands { - public class AddField : AppCommand + public class AddField : AppCommand, IValidatable { public string Name { get; set; } public string Type { get; set; } public JToken Properties { get; set; } + + public void Validate(IList errors) + { + if (!Name.IsSlug()) + { + errors.Add(new ValidationError("Name must be a valid slug", nameof(Name))); + } + + if (string.IsNullOrWhiteSpace(Type)) + { + errors.Add(new ValidationError("Type must be specified", nameof(Type))); + } + + if (Properties != null && !(Properties is JObject)) + { + errors.Add(new ValidationError("Properties must be a object or null.", nameof(Properties))); + } + } } } \ No newline at end of file diff --git a/src/PinkParrot.Write/Schemas/Commands/CreateSchema.cs b/src/PinkParrot.Write/Schemas/Commands/CreateSchema.cs index 30f6d4acf..284eb7353 100644 --- a/src/PinkParrot.Write/Schemas/Commands/CreateSchema.cs +++ b/src/PinkParrot.Write/Schemas/Commands/CreateSchema.cs @@ -6,14 +6,33 @@ // All rights reserved. // ========================================================================== +using System.Collections.Generic; using PinkParrot.Core.Schemas; +using PinkParrot.Infrastructure; namespace PinkParrot.Write.Schemas.Commands { - public class CreateSchema : AppCommand + public class CreateSchema : AppCommand, IValidatable { + private SchemaProperties properties; + public string Name { get; set; } - public SchemaProperties Properties { get; set; } + public SchemaProperties Properties + { + get + { + return properties ?? (properties = new SchemaProperties(null, null)); + } + set { properties = value; } + } + + public void Validate(IList errors) + { + if (!Name.IsSlug()) + { + errors.Add(new ValidationError("Name must be a valid slug", nameof(Name))); + } + } } } \ No newline at end of file diff --git a/src/PinkParrot.Write/Schemas/Commands/UpdateField.cs b/src/PinkParrot.Write/Schemas/Commands/UpdateField.cs index 968da781a..776dc0ec4 100644 --- a/src/PinkParrot.Write/Schemas/Commands/UpdateField.cs +++ b/src/PinkParrot.Write/Schemas/Commands/UpdateField.cs @@ -6,14 +6,24 @@ // All rights reserved. // ========================================================================== +using System.Collections.Generic; using Newtonsoft.Json.Linq; +using PinkParrot.Infrastructure; namespace PinkParrot.Write.Schemas.Commands { - public class UpdateField : AppCommand + public class UpdateField : AppCommand, IValidatable { public long FieldId { get; set; } public JToken Properties { get; set; } + + public void Validate(IList errors) + { + if (!(Properties is JObject)) + { + errors.Add(new ValidationError("Properties must be a object.", nameof(Properties))); + } + } } } \ No newline at end of file diff --git a/src/PinkParrot.Write/Schemas/Commands/UpdateSchema.cs b/src/PinkParrot.Write/Schemas/Commands/UpdateSchema.cs index 781372833..81f5131ce 100644 --- a/src/PinkParrot.Write/Schemas/Commands/UpdateSchema.cs +++ b/src/PinkParrot.Write/Schemas/Commands/UpdateSchema.cs @@ -6,12 +6,22 @@ // All rights reserved. // ========================================================================== +using System.Collections.Generic; using PinkParrot.Core.Schemas; +using PinkParrot.Infrastructure; namespace PinkParrot.Write.Schemas.Commands { - public class UpdateSchema : AppCommand + public class UpdateSchema : AppCommand, IValidatable { public SchemaProperties Properties { get; set; } + + public void Validate(IList errors) + { + if (Properties == null) + { + errors.Add(new ValidationError("Properties must be specified", nameof(Properties))); + } + } } } \ No newline at end of file diff --git a/src/PinkParrot.Write/Schemas/SchemaCommandHandler.cs b/src/PinkParrot.Write/Schemas/SchemaCommandHandler.cs index 83bf725a4..d20448f6a 100644 --- a/src/PinkParrot.Write/Schemas/SchemaCommandHandler.cs +++ b/src/PinkParrot.Write/Schemas/SchemaCommandHandler.cs @@ -51,7 +51,7 @@ namespace PinkParrot.Write.Schemas throw new ValidationException("Cannot create a new schema", error); } - await CreateAsync(command, s => s.Create(command.AppId, command.Name, command.Properties)); + await CreateAsync(command, s => s.Create(command)); } public Task On(DeleteSchema command) @@ -86,7 +86,7 @@ namespace PinkParrot.Write.Schemas public Task On(UpdateSchema command) { - return UpdateAsync(command, s => s.Update(command.Properties)); + return UpdateAsync(command, s => s.Update(command)); } public Task On(AddField command) @@ -94,7 +94,7 @@ namespace PinkParrot.Write.Schemas var propertiesType = registry.FindByTypeName(command.Type).PropertiesType; var propertiesValue = CreateProperties(command.Properties, propertiesType); - return UpdateAsync(command, s => s.AddField(command.Name, propertiesValue)); + return UpdateAsync(command, s => s.AddField(command, propertiesValue)); } public Task On(UpdateField command) @@ -111,7 +111,7 @@ namespace PinkParrot.Write.Schemas var propertiesType = registry.FindByPropertiesType(field.RawProperties.GetType()).PropertiesType; var propertiesValue = CreateProperties(command.Properties, propertiesType); - s.UpdateField(command.FieldId, propertiesValue); + s.UpdateField(command, propertiesValue); }); } diff --git a/src/PinkParrot.Write/Schemas/SchemaDomainObject.cs b/src/PinkParrot.Write/Schemas/SchemaDomainObject.cs index eeb7ffcf3..e94e4c732 100644 --- a/src/PinkParrot.Write/Schemas/SchemaDomainObject.cs +++ b/src/PinkParrot.Write/Schemas/SchemaDomainObject.cs @@ -13,6 +13,8 @@ using PinkParrot.Infrastructure; using PinkParrot.Infrastructure.CQRS; using PinkParrot.Infrastructure.CQRS.Events; using PinkParrot.Infrastructure.Dispatching; +using PinkParrot.Infrastructure.Reflection; +using PinkParrot.Write.Schemas.Commands; namespace PinkParrot.Write.Schemas { @@ -101,32 +103,42 @@ namespace PinkParrot.Write.Schemas isDeleted = false; } - public void AddField(string name, FieldProperties properties) + public void AddField(AddField command, FieldProperties properties) { + Guard.Valid(command, nameof(command), () => $"Cannot add field to schema {Id}"); + Guard.NotNull(properties, nameof(properties)); + VerifyCreatedAndNotDeleted(); - RaiseEvent(new FieldAdded { FieldId = ++totalFields, Name = name, Properties = properties }); + RaiseEvent(new FieldAdded { FieldId = ++totalFields, Name = command.Name, Properties = properties }); } - public void Create(Guid newAppId, string name, SchemaProperties properties) + public void UpdateField(UpdateField command, FieldProperties properties) { - VerifyNotCreated(); - - RaiseEvent(new SchemaCreated { AppId = newAppId, Name = name, Properties = properties }); + Guard.Valid(command, nameof(command), () => $"Cannot update schema '{schema.Name} ({Id})'"); + Guard.NotNull(properties, nameof(properties)); + + VerifyCreatedAndNotDeleted(); + + RaiseEvent(new FieldUpdated { FieldId = command.FieldId, Properties = properties }); } - public void Update(SchemaProperties properties) + public void Create(CreateSchema command) { - VerifyCreatedAndNotDeleted(); - - RaiseEvent(new SchemaUpdated { Properties = properties }); + Guard.Valid(command, nameof(command), () => "Cannot create schema"); + + VerifyNotCreated(); + + RaiseEvent(SimpleMapper.Map(command, new SchemaCreated())); } - public void UpdateField(long fieldId, FieldProperties properties) + public void Update(UpdateSchema command) { + Guard.Valid(command, nameof(command), () => $"Cannot update schema '{schema.Name} ({Id})'"); + VerifyCreatedAndNotDeleted(); - RaiseEvent(new FieldUpdated { FieldId = fieldId, Properties = properties }); + RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); } public void HideField(long fieldId) diff --git a/src/PinkParrot/Configurations/WriteModule.cs b/src/PinkParrot/Configurations/WriteModule.cs index eb66d406f..af2669baf 100644 --- a/src/PinkParrot/Configurations/WriteModule.cs +++ b/src/PinkParrot/Configurations/WriteModule.cs @@ -9,6 +9,7 @@ using Autofac; using PinkParrot.Infrastructure.CQRS.Commands; using PinkParrot.Pipeline.CommandHandlers; +using PinkParrot.Write.Apps; using PinkParrot.Write.Schemas; namespace PinkParrot.Configurations @@ -25,6 +26,14 @@ namespace PinkParrot.Configurations .As() .SingleInstance(); + builder.RegisterType() + .As() + .SingleInstance(); + + builder.RegisterType() + .AsSelf() + .InstancePerDependency(); + builder.RegisterType() .As() .SingleInstance(); diff --git a/src/PinkParrot/Modules/Api/Apps/AppController.cs b/src/PinkParrot/Modules/Api/Apps/AppController.cs new file mode 100644 index 000000000..8b7ac5801 --- /dev/null +++ b/src/PinkParrot/Modules/Api/Apps/AppController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using PinkParrot.Infrastructure.Reflection; +using PinkParrot.Modules.Api.Apps.Models; +using PinkParrot.Modules.Api.Schemas.Models; +using PinkParrot.Read.Apps.Repositories; + +namespace PinkParrot.Modules.Api.Apps +{ + public class AppController + { + private readonly IAppRepository appRepository; + + public AppController(IAppRepository appRepository) + { + this.appRepository = appRepository; + } + + [HttpGet] + [Route("api/schemas/")] + public async Task> Query() + { + var schemas = await appRepository.QueryAllAsync(); + + return schemas.Select(s => SimpleMapper.Map(s, new ListAppDto())).ToList(); + } + } +} diff --git a/src/PinkParrot/Modules/Api/Apps/Models/ListAppDto.cs b/src/PinkParrot/Modules/Api/Apps/Models/ListAppDto.cs new file mode 100644 index 000000000..7753062a2 --- /dev/null +++ b/src/PinkParrot/Modules/Api/Apps/Models/ListAppDto.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// ListAppDto.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; + +namespace PinkParrot.Modules.Api.Apps.Models +{ + public sealed class ListAppDto + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public DateTime Created { get; set; } + + public DateTime LastModified { get; set; } + } +} diff --git a/src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs index 1fed7092a..ef9edd138 100644 --- a/src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs +++ b/src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs @@ -14,11 +14,9 @@ namespace PinkParrot.Modules.Api.Schemas.Models { public class CreateFieldDto { - [Required] [JsonProperty("$type")] public string Type { get; set; } - - [Required] + public string Name { get; set; } public JObject Properties { get; set; } diff --git a/src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs index 21dab2543..58ea0a95f 100644 --- a/src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs +++ b/src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs @@ -6,14 +6,12 @@ // All rights reserved. // ========================================================================== -using System.ComponentModel.DataAnnotations; using PinkParrot.Core.Schemas; namespace PinkParrot.Modules.Api.Schemas.Models { public class CreateSchemaDto { - [Required] public string Name { get; set; } public FieldProperties Properties { get; set; } diff --git a/src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs index 93bd1ecaf..dd78a1bf7 100644 --- a/src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs +++ b/src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs @@ -7,22 +7,17 @@ // ========================================================================== using System; -using System.ComponentModel.DataAnnotations; namespace PinkParrot.Modules.Api.Schemas.Models { public class ListSchemaDto { - [Required] public Guid Id { get; set; } - - [Required] + public string Name { get; set; } - - [Required] + public DateTime Created { get; set; } - - [Required] + public DateTime LastModified { get; set; } } } diff --git a/src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs index 6cbea4b22..e12aaf02e 100644 --- a/src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs +++ b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs @@ -6,14 +6,12 @@ // All rights reserved. // ========================================================================== -using System.ComponentModel.DataAnnotations; using Newtonsoft.Json.Linq; namespace PinkParrot.Modules.Api.Schemas.Models { public class UpdateFieldDto { - [Required] public JObject Properties { get; set; } } } diff --git a/src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs index b7c84e77e..4ca5ff656 100644 --- a/src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs +++ b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs @@ -6,14 +6,12 @@ // All rights reserved. // ========================================================================== -using System.ComponentModel.DataAnnotations; using PinkParrot.Core.Schemas; namespace PinkParrot.Modules.Api.Schemas.Models { public class UpdateSchemaDto { - [Required] public SchemaProperties Properties { get; set; } } } diff --git a/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs b/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs index d5ed99ba0..1cea5fafa 100644 --- a/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs +++ b/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json.Linq; using PinkParrot.Infrastructure.CQRS.Commands; using PinkParrot.Infrastructure.Reflection; using PinkParrot.Modules.Api.Schemas.Models; @@ -31,8 +30,6 @@ namespace PinkParrot.Modules.Api.Schemas { var command = SimpleMapper.Map(model, new AddField()); - command.Properties = command.Properties ?? new JObject(); - return CommandBus.PublishAsync(command); } @@ -40,7 +37,7 @@ namespace PinkParrot.Modules.Api.Schemas [Route("api/schemas/{name}/fields/{fieldId:long}/")] public Task Update(string name, long fieldId, [FromBody] UpdateFieldDto model) { - var command = new UpdateField { FieldId = fieldId, Properties = model.Properties }; + var command = SimpleMapper.Map(model, new UpdateField()); return CommandBus.PublishAsync(command); } diff --git a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs index 47df02b81..d182c7570 100644 --- a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs +++ b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs @@ -62,8 +62,6 @@ namespace PinkParrot.Modules.Api.Schemas { var command = SimpleMapper.Map(model, new CreateSchema { AggregateId = Guid.NewGuid() }); - command.Properties = command.Properties ?? new SchemaProperties(null, null); - await CommandBus.PublishAsync(command); return CreatedAtAction("Query", new EntityCreatedDto { Id = command.AggregateId }); diff --git a/src/PinkParrot/Pipeline/AppMiddleware.cs b/src/PinkParrot/Pipeline/AppMiddleware.cs index 89d575cc8..c304d6996 100644 --- a/src/PinkParrot/Pipeline/AppMiddleware.cs +++ b/src/PinkParrot/Pipeline/AppMiddleware.cs @@ -7,6 +7,7 @@ // ========================================================================== using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using PinkParrot.Read.Apps.Services; @@ -15,22 +16,28 @@ namespace PinkParrot.Pipeline public sealed class AppMiddleware { private readonly IAppProvider appProvider; + private readonly IHostingEnvironment appEnvironment; private readonly RequestDelegate next; - public AppMiddleware(RequestDelegate next, IAppProvider appProvider) + public AppMiddleware(RequestDelegate next, IAppProvider appProvider, IHostingEnvironment appEnvironment) { this.next = next; - this.appProvider = appProvider; + this.appEnvironment = appEnvironment; } public async Task Invoke(HttpContext context) { - var appId = await appProvider.FindAppIdByNameAsync(context.Request.Host.ToString().Split('.')[0]); + var hostParts = context.Request.Host.ToString().Split('.'); - if (appId.HasValue) + if (appEnvironment.IsDevelopment() || hostParts.Length >= 3) { - context.Features.Set(new AppFeature(appId.Value)); + var appId = await appProvider.FindAppIdByNameAsync(hostParts[0]); + + if (appId.HasValue) + { + context.Features.Set(new AppFeature(appId.Value)); + } } await next(context); diff --git a/tests/PinkParrot.Infrastructure.Tests/CQRS/Autofac/AutofacDomainObjectFactoryTests.cs b/tests/PinkParrot.Infrastructure.Tests/CQRS/Autofac/AutofacDomainObjectFactoryTests.cs new file mode 100644 index 000000000..a5e8a1c38 --- /dev/null +++ b/tests/PinkParrot.Infrastructure.Tests/CQRS/Autofac/AutofacDomainObjectFactoryTests.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Autofac; +using PinkParrot.Infrastructure.CQRS.Events; +using Xunit; + +namespace PinkParrot.Infrastructure.CQRS.Autofac +{ + public class AutofacDomainObjectFactoryTests + { + private sealed class DO : DomainObject + { + public DO(Guid id, int version) : base(id, version) + { + } + + protected override void DispatchEvent(Envelope @event) + { + } + } + + [Fact] + public void Should_create_domain_object_with_autofac() + { + var containerBuilder = new ContainerBuilder(); + + containerBuilder.RegisterType() + .AsSelf(); + + var factory = new AutofacDomainObjectFactory(containerBuilder.Build()); + + var id = Guid.NewGuid(); + + var domainObject = factory.CreateNew(typeof(DO), id); + + Assert.Equal(id, domainObject.Id); + Assert.Equal(0, domainObject.Version); + } + } +} diff --git a/tests/PinkParrot.Infrastructure.Tests/GuardTests.cs b/tests/PinkParrot.Infrastructure.Tests/GuardTests.cs index fba187198..f7d8e02dd 100644 --- a/tests/PinkParrot.Infrastructure.Tests/GuardTests.cs +++ b/tests/PinkParrot.Infrastructure.Tests/GuardTests.cs @@ -321,19 +321,19 @@ namespace PinkParrot.Infrastructure [Fact] public void Valid_should_throw_if_null() { - Assert.Throws(() => Guard.Valid(null, "Parameter", "Message")); + Assert.Throws(() => Guard.Valid(null, "Parameter", () => "Message")); } [Fact] public void Valid_should_throw_if_invalid() { - Assert.Throws(() => Guard.Valid(new ValidatableInvalid(), "Parameter", "Message")); + Assert.Throws(() => Guard.Valid(new ValidatableInvalid(), "Parameter", () => "Message")); } [Fact] public void Valid_should_do_nothing_if_valid() { - Guard.Valid(new ValidatableValid(), "Parameter", "Message"); + Guard.Valid(new ValidatableValid(), "Parameter", () => "Message"); } } } \ No newline at end of file diff --git a/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs b/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs index 8af8b9603..26acf3793 100644 --- a/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs +++ b/tests/PinkParrot.Write.Tests/Apps/AppDomainObjectTest.cs @@ -11,27 +11,43 @@ using PinkParrot.Infrastructure; using PinkParrot.Write.Apps; using PinkParrot.Write.Apps.Commands; using Xunit; +using System.Linq; +using FluentAssertions; +using PinkParrot.Events.Apps; namespace PinkParrot.Write.Tests.Apps { public class AppDomainObjectTest { + private const string TestName = "app"; private readonly AppDomainObject sut = new AppDomainObject(Guid.NewGuid(), 0); [Fact] public void Create_should_throw_if_created() { - sut.Create(new CreateApp { Name = "app" }); + sut.Create(new CreateApp { Name = TestName }); - Assert.Throws(() => sut.Create(new CreateApp { Name = "app" })); + Assert.Throws(() => sut.Create(new CreateApp { Name = TestName })); + } + + [Fact] + public void Create_should_throw_if_command_is_invalid() + { + Assert.Throws(() => sut.Create(new CreateApp())); } [Fact] public void Create_should_specify_name() { - sut.Create(new CreateApp { Name = "app" }); + sut.Create(new CreateApp { Name = TestName }); + + Assert.Equal(TestName, sut.Name); - Assert.Equal("app", sut.Name); + sut.GetUncomittedEvents() + .Select(x => x.Payload as AppCreated) + .Single() + .ShouldBeEquivalentTo( + new AppCreated { Name = TestName }); } } } diff --git a/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj b/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj index d598820f2..947e76d11 100644 --- a/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj +++ b/tests/PinkParrot.Write.Tests/PinkParrot.Write.Tests.xproj @@ -15,5 +15,8 @@ 2.0 + + + \ No newline at end of file diff --git a/tests/PinkParrot.Write.Tests/Schemas/SchemaDomainObjectTest.cs b/tests/PinkParrot.Write.Tests/Schemas/SchemaDomainObjectTest.cs new file mode 100644 index 000000000..fdf40b873 --- /dev/null +++ b/tests/PinkParrot.Write.Tests/Schemas/SchemaDomainObjectTest.cs @@ -0,0 +1,106 @@ +// ========================================================================== +// SchemaDomainObjectTest.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Linq; +using FluentAssertions; +using PinkParrot.Core.Schemas; +using PinkParrot.Events.Schemas; +using PinkParrot.Infrastructure; +using PinkParrot.Write.Schemas; +using PinkParrot.Write.Schemas.Commands; +using Xunit; + +namespace PinkParrot.Write.Tests.Schemas +{ + [Collection("Schema")] + public class SchemaDomainObjectTest + { + private const string TestName = "schema"; + private readonly Guid appId = Guid.NewGuid(); + private readonly FieldRegistry registry = new FieldRegistry(); + private readonly SchemaDomainObject sut; + + public SchemaDomainObjectTest() + { + sut = new SchemaDomainObject(Guid.NewGuid(), 0, registry); + } + + [Fact] + public void Create_should_throw_if_created() + { + sut.Create(new CreateSchema { Name = TestName }); + + Assert.Throws(() => sut.Create(new CreateSchema { Name = TestName })); + } + + [Fact] + public void Create_should_throw_if_command_is_invalid() + { + Assert.Throws(() => sut.Create(new CreateSchema())); + } + + [Fact] + public void Create_should_create_schema() + { + var props = new SchemaProperties(null, null); + + sut.Create(new CreateSchema { Name = TestName, AppId = appId, Properties = props }); + + Assert.Equal("schema", sut.Schema.Name); + Assert.Equal(props, sut.Schema.Properties); + Assert.Equal(appId, sut.AppId); + + sut.GetUncomittedEvents() + .Select(x => x.Payload as SchemaCreated) + .Single() + .ShouldBeEquivalentTo( + new SchemaCreated { Name = TestName, AppId = appId, Properties = props }); + } + + [Fact] + public void Update_should_throw_if_not_created() + { + Assert.Throws(() => sut.Update(new UpdateSchema { Properties = new SchemaProperties(null, null) })); + } + + [Fact] + public void Update_should_throw_if_command_is_deleted() + { + sut.Create(new CreateSchema { Name = TestName }); + sut.Delete(); + + Assert.Throws(() => sut.Update(new UpdateSchema())); + } + + [Fact] + public void Update_should_throw_if_command_is_invalid() + { + sut.Create(new CreateSchema { Name = TestName }); + + Assert.Throws(() => sut.Update(new UpdateSchema())); + } + + [Fact] + public void Update_should_refresh_properties() + { + var props = new SchemaProperties(null, null); + + sut.Create(new CreateSchema { Name = TestName, AppId = appId }); + sut.Update(new UpdateSchema { Properties = props }); + + Assert.Equal(props, sut.Schema.Properties); + + sut.GetUncomittedEvents() + .Select(x => x.Payload as SchemaUpdated) + .Last() + .ShouldBeEquivalentTo( + new SchemaUpdated { Properties = props }); + } + } +} diff --git a/tests/PinkParrot.Write.Tests/Utils/SchemaFixture.cs b/tests/PinkParrot.Write.Tests/Utils/SchemaFixture.cs new file mode 100644 index 000000000..18395f27d --- /dev/null +++ b/tests/PinkParrot.Write.Tests/Utils/SchemaFixture.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// SchemaFixture.cs +// PinkParrot Headless CMS +// ========================================================================== +// Copyright (c) PinkParrot Group +// All rights reserved. +// ========================================================================== + +using PinkParrot.Core.Schemas; +using PinkParrot.Infrastructure; +using System.Reflection; +using Xunit; + +namespace PinkParrot.Write.Tests.Utils +{ + public class SchemaFixture + { + public SchemaFixture() + { + TypeNameRegistry.Map(typeof(Schema).GetTypeInfo().Assembly); + } + } + + [CollectionDefinition("Schema")] + public class DatabaseCollection : ICollectionFixture + { + } +} diff --git a/tests/PinkParrot.Write.Tests/project.json b/tests/PinkParrot.Write.Tests/project.json index 5a74dc793..07014c74a 100644 --- a/tests/PinkParrot.Write.Tests/project.json +++ b/tests/PinkParrot.Write.Tests/project.json @@ -7,6 +7,7 @@ } }, "dependencies": { + "FluentAssertions": "4.15.0", "dotnet-test-xunit": "2.2.0-preview2-build1029", "PinkParrot.Core": "1.0.0-*", "PinkParrot.Infrastructure": "1.0.0-*", diff --git a/src/RunCoverage.bat b/tests/RunCoverage.bat similarity index 87% rename from src/RunCoverage.bat rename to tests/RunCoverage.bat index 3610fcf58..d422e175f 100644 --- a/src/RunCoverage.bat +++ b/tests/RunCoverage.bat @@ -19,7 +19,7 @@ exit /b %errorlevel% "%UserProfile%\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ^ -register:user ^ -target:"C:\Program Files\dotnet\dotnet.exe" ^ --targetargs:"test %~dp0\pinkparrot_infrastructure\PinkParrot.Infrastructure.Tests" ^ +-targetargs:"test %~dp0\PinkParrot.Infrastructure.Tests" ^ -filter:"+[PinkParrot*]*" ^ -skipautoprops ^ -output:"%~dp0\GeneratedReports\Infrastructure.xml" ^ @@ -28,7 +28,7 @@ exit /b %errorlevel% "%UserProfile%\.nuget\packages\OpenCover\4.6.519\tools\OpenCover.Console.exe" ^ -register:user ^ -target:"C:\Program Files\dotnet\dotnet.exe" ^ --targetargs:"test %~dp0\pinkparrot_write\PinkParrot.Write.Tests" ^ +-targetargs:"test %~dp0\PinkParrot.Write.Tests" ^ -filter:"+[PinkParrot*]*" ^ -skipautoprops ^ -output:"%~dp0\GeneratedReports\Write.xml" ^