diff --git a/.gitignore b/.gitignore
index 3a2238d6b..8ad573c14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -242,4 +242,7 @@ ModelManifest.xml
.paket/paket.exe
# FAKE - F# Make
-.fake/
\ No newline at end of file
+.fake/
+
+# OpenCover
+GeneratedReports/
\ No newline at end of file
diff --git a/PinkParrot.sln.DotSettings b/PinkParrot.sln.DotSettings
index 94cc32e31..da6c0b707 100644
--- a/PinkParrot.sln.DotSettings
+++ b/PinkParrot.sln.DotSettings
@@ -90,4 +90,5 @@
True
True
True
+ True
True
\ No newline at end of file
diff --git a/src/PinkParrot/Modules/Api/ErrorDto.cs b/src/PinkParrot/Modules/Api/ErrorDto.cs
index 18ae1e68e..1ec56b443 100644
--- a/src/PinkParrot/Modules/Api/ErrorDto.cs
+++ b/src/PinkParrot/Modules/Api/ErrorDto.cs
@@ -17,6 +17,6 @@ namespace PinkParrot.Modules.Api
public string[] Details { get; set; }
- public int? StatusCode { get; set; }
+ public int? StatusCode { get; set; } = 400;
}
}
diff --git a/src/PinkParrot/Modules/Api/Schemas/CreateFieldDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs
similarity index 85%
rename from src/PinkParrot/Modules/Api/Schemas/CreateFieldDto.cs
rename to src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs
index ee359419e..1fed7092a 100644
--- a/src/PinkParrot/Modules/Api/Schemas/CreateFieldDto.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/Models/CreateFieldDto.cs
@@ -10,18 +10,17 @@ using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace PinkParrot.Modules.Api.Schemas
+namespace PinkParrot.Modules.Api.Schemas.Models
{
public class CreateFieldDto
{
[Required]
[JsonProperty("$type")]
- public string Name { get; set; }
-
- [Required]
public string Type { get; set; }
[Required]
- public JToken Properties { get; set; }
+ public string Name { get; set; }
+
+ public JObject Properties { get; set; }
}
}
diff --git a/src/PinkParrot/Modules/Api/Schemas/CreateSchemaDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs
similarity index 90%
rename from src/PinkParrot/Modules/Api/Schemas/CreateSchemaDto.cs
rename to src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs
index 13b32e241..21dab2543 100644
--- a/src/PinkParrot/Modules/Api/Schemas/CreateSchemaDto.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/Models/CreateSchemaDto.cs
@@ -9,14 +9,13 @@
using System.ComponentModel.DataAnnotations;
using PinkParrot.Core.Schemas;
-namespace PinkParrot.Modules.Api.Schemas
+namespace PinkParrot.Modules.Api.Schemas.Models
{
public class CreateSchemaDto
{
[Required]
public string Name { get; set; }
-
- [Required]
+
public FieldProperties Properties { get; set; }
}
}
diff --git a/src/PinkParrot/Modules/Api/Schemas/ListSchemaDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs
similarity index 93%
rename from src/PinkParrot/Modules/Api/Schemas/ListSchemaDto.cs
rename to src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs
index 8bbbb5ff2..93bd1ecaf 100644
--- a/src/PinkParrot/Modules/Api/Schemas/ListSchemaDto.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/Models/ListSchemaDto.cs
@@ -9,7 +9,7 @@
using System;
using System.ComponentModel.DataAnnotations;
-namespace PinkParrot.Modules.Api.Schemas
+namespace PinkParrot.Modules.Api.Schemas.Models
{
public class ListSchemaDto
{
diff --git a/src/PinkParrot/Modules/Api/Schemas/UpdateFieldDto.cs b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs
similarity index 83%
rename from src/PinkParrot/Modules/Api/Schemas/UpdateFieldDto.cs
rename to src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs
index d558c0e95..6cbea4b22 100644
--- a/src/PinkParrot/Modules/Api/Schemas/UpdateFieldDto.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateFieldDto.cs
@@ -9,11 +9,11 @@
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json.Linq;
-namespace PinkParrot.Modules.Api.Schemas
+namespace PinkParrot.Modules.Api.Schemas.Models
{
public class UpdateFieldDto
{
[Required]
- public JToken Properties { get; set; }
+ public JObject Properties { get; set; }
}
}
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/IFieldProperties.cs b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs
similarity index 57%
rename from src/pinkparrot_core/PinkParrot.Core/Schemas/IFieldProperties.cs
rename to src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs
index 4855988a6..b7c84e77e 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/IFieldProperties.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/Models/UpdateSchemaDto.cs
@@ -1,16 +1,19 @@
// ==========================================================================
-// IFieldProperties.cs
+// UpdateSchemaDto.cs
// PinkParrot Headless CMS
// ==========================================================================
// Copyright (c) PinkParrot Group
// All rights reserved.
// ==========================================================================
-using PinkParrot.Infrastructure;
+using System.ComponentModel.DataAnnotations;
+using PinkParrot.Core.Schemas;
-namespace PinkParrot.Core.Schemas
+namespace PinkParrot.Modules.Api.Schemas.Models
{
- public interface IFieldProperties : IValidatable
+ 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 be171b58b..d5ed99ba0 100644
--- a/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/SchemaFieldsController.cs
@@ -8,12 +8,16 @@
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;
+using PinkParrot.Pipeline;
using PinkParrot.Write.Schemas.Commands;
namespace PinkParrot.Modules.Api.Schemas
{
+ [ApiExceptionFilter]
public class SchemasFieldsController : ControllerBase
{
public SchemasFieldsController(ICommandBus commandBus)
@@ -27,6 +31,8 @@ namespace PinkParrot.Modules.Api.Schemas
{
var command = SimpleMapper.Map(model, new AddField());
+ command.Properties = command.Properties ?? new JObject();
+
return CommandBus.PublishAsync(command);
}
diff --git a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
index 1baaa45b2..35da7592f 100644
--- a/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
+++ b/src/PinkParrot/Modules/Api/Schemas/SchemasController.cs
@@ -14,12 +14,15 @@ using Microsoft.AspNetCore.Mvc;
using PinkParrot.Core.Schemas;
using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Infrastructure.Reflection;
+using PinkParrot.Modules.Api.Schemas.Models;
+using PinkParrot.Pipeline;
using PinkParrot.Read.Models;
using PinkParrot.Read.Repositories;
using PinkParrot.Write.Schemas.Commands;
namespace PinkParrot.Modules.Api.Schemas
{
+ [ApiExceptionFilter]
public class SchemasController : ControllerBase
{
private readonly ISchemaRepository schemaRepository;
@@ -59,6 +62,8 @@ 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 });
@@ -66,9 +71,9 @@ namespace PinkParrot.Modules.Api.Schemas
[HttpPut]
[Route("api/schemas/{name}/")]
- public async Task Update(string name, [FromBody] SchemaProperties schema)
+ public async Task Update(string name, [FromBody] UpdateSchemaDto model)
{
- var command = new UpdateSchema { Properties = schema };
+ var command = SimpleMapper.Map(model, new UpdateSchema());
await CommandBus.PublishAsync(command);
diff --git a/src/PinkParrot/Pipeline/ApiExceptionFilterAttribute.cs b/src/PinkParrot/Pipeline/ApiExceptionFilterAttribute.cs
new file mode 100644
index 000000000..2a3362b5a
--- /dev/null
+++ b/src/PinkParrot/Pipeline/ApiExceptionFilterAttribute.cs
@@ -0,0 +1,77 @@
+// ==========================================================================
+// ExceptionFilter.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using PinkParrot.Infrastructure;
+using PinkParrot.Modules.Api;
+
+// ReSharper disable InvertIf
+
+namespace PinkParrot.Pipeline
+{
+ public class ApiExceptionFilterAttribute : ActionFilterAttribute, IExceptionFilter
+ {
+ private static readonly List> handlers = new List>();
+
+ private static void AddHandler(Func handler) where T : Exception
+ {
+ handlers.Add(ex =>
+ {
+ var typed = ex as T;
+
+ return typed != null ? handler(typed) : null;
+ });
+ }
+
+ static ApiExceptionFilterAttribute()
+ {
+ AddHandler(ex =>
+ new NotFoundResult());
+
+ AddHandler(ex =>
+ new BadRequestObjectResult(new ErrorDto { Message = ex.Message }));
+
+ AddHandler(ex =>
+ new BadRequestObjectResult(new ErrorDto { Message = ex.Message, Details = ex.Errors.Select(e => e.Message).ToArray() }));
+ }
+
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ if (!context.ModelState.IsValid)
+ {
+ var errors = context.ModelState.Values.SelectMany(g => g.Errors).Select(e => new ValidationError(e.ErrorMessage)).ToList();
+
+ throw new ValidationException("The model is not valid.", errors);
+ }
+ }
+
+ public void OnException(ExceptionContext context)
+ {
+ IActionResult result = null;
+
+ foreach (var handler in handlers)
+ {
+ result = handler(context.Exception);
+
+ if (result != null)
+ {
+ break;
+ }
+ }
+
+ if (result != null)
+ {
+ context.Result = result;
+ }
+ }
+ }
+}
diff --git a/src/PinkParrot/project.json b/src/PinkParrot/project.json
index a1d0603c9..c1cabf022 100644
--- a/src/PinkParrot/project.json
+++ b/src/PinkParrot/project.json
@@ -22,11 +22,13 @@
"type": "platform"
},
"MongoDB.Driver": "2.3.0-rc1",
+ "OpenCover": "4.6.519",
"PinkParrot.Core": "1.0.0-*",
"PinkParrot.Events": "1.0.0-*",
"PinkParrot.Infrastructure": "1.0.0-*",
"PinkParrot.Read": "1.0.0-*",
- "PinkParrot.Write": "1.0.0-*"
+ "PinkParrot.Write": "1.0.0-*",
+ "ReportGenerator": "2.4.5"
},
"tools": {
diff --git a/src/RunCoverage.bat b/src/RunCoverage.bat
new file mode 100644
index 000000000..fd8216ef3
--- /dev/null
+++ b/src/RunCoverage.bat
@@ -0,0 +1,37 @@
+REM Create a 'GeneratedReports' folder if it does not exist
+if not exist "%~dp0GeneratedReports" mkdir "%~dp0GeneratedReports"
+
+REM Remove any previously created test output directories
+CD %~dp0
+FOR /D /R %%X IN (%USERNAME%*) DO RD /S /Q "%%X"
+
+REM Run the tests against the targeted output
+call :RunOpenCoverUnitTestMetrics
+
+REM Generate the report output based on the test results
+if %errorlevel% equ 0 (
+ call :RunReportGeneratorOutput
+)
+
+REM Launch the report
+if %errorlevel% equ 0 (
+ call :RunLaunchReport
+)
+exit /b %errorlevel%
+
+:RunOpenCoverUnitTestMetrics
+"%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" ^
+-filter:"+[PinkParrot*]*" ^
+-skipautoprops ^
+-output:"%~dp0\GeneratedReports\Infrastructure.xml" ^
+-oldStyle
+exit /b %errorlevel%
+
+:RunReportGeneratorOutput
+"%UserProfile%\.nuget\packages\ReportGenerator\2.4.5\tools\ReportGenerator.exe" ^
+-reports:"%~dp0\GeneratedReports\Infrastructure.xml" ^
+-targetdir:"%~dp0\GeneratedReports\Output"
+exit /b %errorlevel%
\ No newline at end of file
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/Field.cs b/src/pinkparrot_core/PinkParrot.Core/Schemas/Field.cs
index 129b9d412..20b93d31c 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/Field.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schemas/Field.cs
@@ -34,6 +34,16 @@ namespace PinkParrot.Core.Schemas
get { return name; }
}
+ public string Hints
+ {
+ get { return RawProperties.Hints; }
+ }
+
+ public string Label
+ {
+ get { return RawProperties.Label; }
+ }
+
public bool IsHidden
{
get { return isHidden; }
@@ -44,13 +54,12 @@ namespace PinkParrot.Core.Schemas
get { return isDisabled; }
}
- public abstract IFieldProperties RawProperties { get; }
-
- public abstract string Label { get; }
-
- public abstract string Hints { get; }
+ public bool IsRequired
+ {
+ get { return RawProperties.IsRequired; }
+ }
- public abstract bool IsRequired { get; }
+ public abstract FieldProperties RawProperties { get; }
protected Field(long id, string name)
{
@@ -62,7 +71,7 @@ namespace PinkParrot.Core.Schemas
this.name = name;
}
- public abstract Field Update(IFieldProperties newProperties);
+ public abstract Field Update(FieldProperties newProperties);
public Task ValidateAsync(PropertyValue property, ICollection errors)
{
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldProperties.cs b/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldProperties.cs
index ee00d2ce4..edac1223a 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldProperties.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldProperties.cs
@@ -11,7 +11,7 @@ using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schemas
{
- public abstract class FieldProperties : NamedElementProperties, IFieldProperties
+ public abstract class FieldProperties : NamedElementProperties, IValidatable
{
public bool IsRequired { get; }
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldRegistry.cs b/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldRegistry.cs
index f2756e4a9..2090534b0 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldRegistry.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schemas/FieldRegistry.cs
@@ -12,7 +12,7 @@ using PinkParrot.Infrastructure;
namespace PinkParrot.Core.Schemas
{
- public delegate Field FactoryFunction(long id, string name, IFieldProperties properties);
+ public delegate Field FactoryFunction(long id, string name, FieldProperties properties);
public sealed class FieldRegistry
{
@@ -43,7 +43,7 @@ namespace PinkParrot.Core.Schemas
this.propertiesType = propertiesType;
}
- Field IRegisteredField.CreateField(long id, string name, IFieldProperties properties)
+ Field IRegisteredField.CreateField(long id, string name, FieldProperties properties)
{
return fieldFactory(id, name, properties);
}
@@ -64,7 +64,7 @@ namespace PinkParrot.Core.Schemas
fieldsByPropertyType[registered.PropertiesType] = registered;
}
- public Field CreateField(long id, string name, IFieldProperties properties)
+ public Field CreateField(long id, string name, FieldProperties properties)
{
var registered = fieldsByPropertyType[properties.GetType()];
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/Field_Generic.cs b/src/pinkparrot_core/PinkParrot.Core/Schemas/Field_Generic.cs
index b1a1e0ab7..10f9a66c2 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/Field_Generic.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schemas/Field_Generic.cs
@@ -15,26 +15,11 @@ namespace PinkParrot.Core.Schemas
{
private T properties;
- public override IFieldProperties RawProperties
+ public override FieldProperties RawProperties
{
get { return properties; }
}
- public override string Label
- {
- get { return properties.Label ?? Name; }
- }
-
- public override string Hints
- {
- get { return properties.Hints; }
- }
-
- public override bool IsRequired
- {
- get { return properties.IsRequired; }
- }
-
public T Properties
{
get { return properties; }
@@ -48,7 +33,7 @@ namespace PinkParrot.Core.Schemas
this.properties = properties;
}
- public override Field Update(IFieldProperties newProperties)
+ public override Field Update(FieldProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/IRegisteredField.cs b/src/pinkparrot_core/PinkParrot.Core/Schemas/IRegisteredField.cs
index 78cf8ca40..cd1f2a00f 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/IRegisteredField.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schemas/IRegisteredField.cs
@@ -14,6 +14,6 @@ namespace PinkParrot.Core.Schemas
{
Type PropertiesType { get; }
- Field CreateField(long id, string name, IFieldProperties properties);
+ Field CreateField(long id, string name, FieldProperties properties);
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_core/PinkParrot.Core/Schemas/Schema.cs b/src/pinkparrot_core/PinkParrot.Core/Schemas/Schema.cs
index 5ce45e5dd..cf9398e77 100644
--- a/src/pinkparrot_core/PinkParrot.Core/Schemas/Schema.cs
+++ b/src/pinkparrot_core/PinkParrot.Core/Schemas/Schema.cs
@@ -55,8 +55,6 @@ namespace PinkParrot.Core.Schemas
public static Schema Create(string name, SchemaProperties newProperties)
{
- newProperties = newProperties ?? new SchemaProperties(null, null);
-
if (!name.IsSlug())
{
var error = new ValidationError("Name must be a valid slug", "Name");
@@ -86,7 +84,7 @@ namespace PinkParrot.Core.Schemas
return new Schema(name, properties, fieldsById.SetItem(field.Id, field));
}
- public Schema UpdateField(long fieldId, IFieldProperties newProperties)
+ public Schema UpdateField(long fieldId, FieldProperties newProperties)
{
return UpdateField(fieldId, field => field.Update(newProperties));
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldAdded.cs b/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldAdded.cs
index 5dd9d7afe..f706561c8 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldAdded.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldAdded.cs
@@ -18,6 +18,6 @@ namespace PinkParrot.Events.Schemas
public string Name { get; set; }
- public IFieldProperties Properties { get; set; }
+ public FieldProperties Properties { get; set; }
}
}
diff --git a/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldUpdated.cs b/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldUpdated.cs
index e857ecc28..7452979b0 100644
--- a/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldUpdated.cs
+++ b/src/pinkparrot_events/PinkParrot.Events/Schemas/FieldUpdated.cs
@@ -16,6 +16,6 @@ namespace PinkParrot.Events.Schemas
{
public long FieldId { get; set; }
- public IFieldProperties Properties { get; set; }
+ public FieldProperties Properties { get; set; }
}
}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs
index 03f863e53..464571c03 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/CollectionExtensionsTests.cs
@@ -112,6 +112,14 @@ namespace PinkParrot.Infrastructure
Assert.Equal(list, listDictionary[12]);
}
+ [Fact]
+ public void SequentialHashCode_should_ignore_null_values()
+ {
+ var collection = new string[] { null, null };
+
+ Assert.Equal(17, collection.SequentialHashCode());
+ }
+
[Fact]
public void SequentialHashCode_should_return_same_hash_codes_for_list_with_same_order()
{
@@ -165,5 +173,55 @@ namespace PinkParrot.Infrastructure
Assert.Equal(collection2.OrderedHashCode(), collection1.OrderedHashCode());
}
+
+ [Fact]
+ public void EqualsDictionary_should_return_true_for_equal_dictionaries()
+ {
+ var lhs = new Dictionary
+ {
+ [1] = 1,
+ [2] = 2
+ };
+ var rhs = new Dictionary
+ {
+ [1] = 1,
+ [2] = 2
+ };
+
+ Assert.True(lhs.EqualsDictionary(rhs));
+ }
+
+ [Fact]
+ public void EqualsDictionary_should_return_false_for_different_sizes()
+ {
+ var lhs = new Dictionary
+ {
+ [1] = 1,
+ [2] = 2
+ };
+ var rhs = new Dictionary
+ {
+ [1] = 1
+ };
+
+ Assert.False(lhs.EqualsDictionary(rhs));
+ }
+
+ [Fact]
+ public void EqualsDictionary_should_return_false_for_different_values()
+ {
+ var lhs = new Dictionary
+ {
+ [1] = 1,
+ [2] = 2
+ };
+ var rhs = new Dictionary
+ {
+ [1] = 1,
+ [3] = 3
+ };
+
+ Assert.False(lhs.EqualsDictionary(rhs));
+ }
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/GuardTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/GuardTests.cs
index ec4417a14..763545628 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/GuardTests.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/GuardTests.cs
@@ -16,85 +16,103 @@ namespace PinkParrot.Infrastructure
[Theory]
[InlineData("")]
[InlineData(" ")]
- public void Should_throw_when_target_is_null_for_empty_string(string invalidString)
+ public void NotNullOrEmpty_should_throw_for_empy_strings(string invalidString)
{
Assert.Throws(() => Guard.NotNullOrEmpty(invalidString, "parameter"));
}
[Fact]
- public void Should_do_nothing_if_target_string_is_valid()
+ public void NotNullOrEmpty_should_throw_for_null_string()
+ {
+ Assert.Throws(() => Guard.NotNullOrEmpty(null, "parameter"));
+ }
+
+ [Fact]
+ public void NotNullOrEmpty_should_do_nothing_for_vaid_string()
{
Guard.NotNullOrEmpty("value", "parameter");
}
[Fact]
- public void Should_do_nothing_if_target_is_not_null()
+ public void NotNull_should_throw_for_null_value()
{
- Guard.NotNull("value", "parameter");
+ Assert.Throws(() => Guard.NotNull(null, "parameter"));
}
[Fact]
- public void Should_do_nothing_if_enum_is_valid()
+ public void NotNull_should_do_nothing_for_valid_value()
{
- Guard.Enum(DateTimeKind.Local, "Parameter");
+ Guard.NotNull("value", "parameter");
}
[Fact]
- public void Should_throw_if_enum_is_not_valid()
+ public void Enum_should_throw_for_invalid_enum()
{
Assert.Throws(() => Guard.Enum((DateTimeKind)13, "Parameter"));
}
[Fact]
- public void Should_do_nothing_when_guid_is_not_empty()
+ public void Enum_should_do_nothing_for_valid_enum()
{
- Guard.NotEmpty(Guid.NewGuid(), "parameter");
+ Guard.Enum(DateTimeKind.Local, "Parameter");
}
[Fact]
- public void Should_throw_when_guid_is_empty()
+ public void NotEmpty_should_throw_for_empty_guid()
{
Assert.Throws(() => Guard.NotEmpty(Guid.Empty, "parameter"));
}
[Fact]
- public void Should_throw_when_target_is_null()
+ public void NotEmpty_should_do_nothing_for_valid_guid()
{
- Assert.Throws(() => Guard.NotNull(null, "parameter"));
+ Guard.NotEmpty(Guid.NewGuid(), "parameter");
}
[Fact]
- public void Should_throw_when_target_is_null_for_null_string()
+ public void HasType_should_throw_for_other_type()
{
- Assert.Throws(() => Guard.NotNullOrEmpty(null, "parameter"));
+ Assert.Throws(() => Guard.HasType("value", "parameter"));
}
[Fact]
- public void Should_do_nothing_when_target_has_correct_type()
+ public void HasType_should_do_nothing_for_null_value()
+ {
+ Guard.HasType(null, "parameter");
+ }
+
+ [Fact]
+ public void HasType_should_do_nothing_for_correct_type()
{
Guard.HasType(123, "parameter");
}
[Fact]
- public void Should_throw_when_target_has_wrong_type()
+ public void HasType_nongeneric_should_throw_for_other_type()
{
- Assert.Throws(() => Guard.HasType("value", "parameter"));
+ Assert.Throws(() => Guard.HasType("value", typeof(int), "parameter"));
}
[Fact]
- public void Should_throw_when_checking_for_null_and_target_is_null()
+ public void HasType_nongeneric_should_do_nothing_for_null_value()
{
- Assert.Throws(() => Guard.HasType(null, "parameter"));
+ Guard.HasType(null, typeof(int), "parameter");
}
[Fact]
- public void Should_do_nothing_when_target_is_not_default_value()
+ public void HasType_nongeneric_should_do_nothing_for_correct_type()
{
- Guard.NotDefault(Guid.NewGuid(), "parameter");
+ Guard.HasType(123, typeof(int), "parameter");
}
[Fact]
- public void Should_throw_exception_when_value_has_default()
+ public void HasType_nongeneric_should_do_nothing_for_null_type()
+ {
+ Guard.HasType(123, null, "parameter");
+ }
+
+ [Fact]
+ public void NotDefault_should_throw_for_default_values()
{
Assert.Throws(() => Guard.NotDefault(Guid.Empty, "parameter"));
Assert.Throws(() => Guard.NotDefault(0, "parameter"));
@@ -102,6 +120,12 @@ namespace PinkParrot.Infrastructure
Assert.Throws(() => Guard.NotDefault(false, "parameter"));
}
+ [Fact]
+ public void NotDefault_should_do_nothing_for_non_default_value()
+ {
+ Guard.NotDefault(Guid.NewGuid(), "parameter");
+ }
+
[Theory]
[InlineData("")]
[InlineData(" ")]
@@ -110,7 +134,7 @@ namespace PinkParrot.Infrastructure
[InlineData(" not-a-slug ")]
[InlineData("-not-a-slug-")]
[InlineData("not$-a-slug")]
- public void Should_throw_exception_for_invalid_slug(string slug)
+ public void ValidSlug_should_throw_for_invalid_slugs(string slug)
{
Assert.Throws(() => Guard.ValidSlug(slug, "slug"));
}
@@ -120,9 +144,162 @@ namespace PinkParrot.Infrastructure
[InlineData("slug23")]
[InlineData("other-slug")]
[InlineData("just-another-slug")]
- public void Should_do_nothing_for_valid_slug(string slug)
+ public void ValidSlug_should_do_nothing_for_valid_slugs(string slug)
{
Guard.ValidSlug(slug, "parameter");
}
+
+ [Theory]
+ [InlineData(double.PositiveInfinity)]
+ [InlineData(double.NegativeInfinity)]
+ [InlineData(double.NaN)]
+ public void ValidNumber_should_throw_for_invalid_doubles(double value)
+ {
+ Assert.Throws(() => Guard.ValidNumber(value, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(0d)]
+ [InlineData(-1000d)]
+ [InlineData(1000d)]
+ public void ValidNumber_do_nothing_for_valid_double(double value)
+ {
+ Guard.ValidNumber(value, "parameter");
+ }
+
+ [Theory]
+ [InlineData(float.PositiveInfinity)]
+ [InlineData(float.NegativeInfinity)]
+ [InlineData(float.NaN)]
+ public void ValidNumber_should_throw_for_invalid_float(float value)
+ {
+ Assert.Throws(() => Guard.ValidNumber(value, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(0f)]
+ [InlineData(-1000f)]
+ [InlineData(1000f)]
+ public void ValidNumber_do_nothing_for_valid_float(float value)
+ {
+ Guard.ValidNumber(value, "parameter");
+ }
+
+ [Theory]
+ [InlineData(4)]
+ [InlineData(104)]
+ public void Between_should_throw_for_values_outside_of_range(int value)
+ {
+ Assert.Throws(() => Guard.Between(value, 10, 100, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(10)]
+ [InlineData(55)]
+ [InlineData(100)]
+ public void Between_should_do_nothing_for_values_in_range(int value)
+ {
+ Guard.Between(value, 10, 100, "parameter");
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(100)]
+ public void GreaterThan_should_throw_for_smaller_values(int value)
+ {
+ Assert.Throws(() => Guard.GreaterThan(value, 100, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(101)]
+ [InlineData(200)]
+ public void GreaterThan_should_do_nothing_for_greater_values(int value)
+ {
+ Guard.GreaterThan(value, 100, "parameter");
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(99)]
+ public void GreaterEquals_should_throw_for_smaller_values(int value)
+ {
+ Assert.Throws(() => Guard.GreaterEquals(value, 100, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(100)]
+ [InlineData(200)]
+ public void GreaterEquals_should_do_nothing_for_greater_values(int value)
+ {
+ Guard.GreaterEquals(value, 100, "parameter");
+ }
+
+ [Theory]
+ [InlineData(1000)]
+ [InlineData(100)]
+ public void LessThan_should_throw_for_greater_values(int value)
+ {
+ Assert.Throws(() => Guard.LessThan(value, 100, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(99)]
+ [InlineData(50)]
+ public void LessThan_should_do_nothing_for_smaller_values(int value)
+ {
+ Guard.LessThan(value, 100, "parameter");
+ }
+
+ [Theory]
+ [InlineData(1000)]
+ [InlineData(101)]
+ public void LessEquals_should_throw_for_greater_values(int value)
+ {
+ Assert.Throws(() => Guard.LessEquals(value, 100, "parameter"));
+ }
+
+ [Theory]
+ [InlineData(100)]
+ [InlineData(50)]
+ public void LessEquals_should_do_nothing_for_smaller_values(int value)
+ {
+ Guard.LessEquals(value, 100, "parameter");
+ }
+
+ [Fact]
+ public void NotEmpty_should_throw_for_empty_collection()
+ {
+ Assert.Throws(() => Guard.NotEmpty(new int[0], "parameter"));
+ }
+
+ [Fact]
+ public void NotEmpty_should_throw_for_null_collection()
+ {
+ Assert.Throws(() => Guard.NotEmpty((int[])null, "parameter"));
+ }
+
+ [Fact]
+ public void NotEmpty_should_do_nothing_for_value_collection()
+ {
+ Guard.NotEmpty(new [] { 1, 2, 3 }, "parameter");
+ }
+
+ [Fact]
+ public void ValidFileName_should_throw_for_invalid_file_name()
+ {
+ Assert.Throws(() => Guard.ValidFileName("File/Name", "Parameter"));
+ }
+
+ [Fact]
+ public void ValidFileName_should_throw_for_null_file_name()
+ {
+ Assert.Throws(() => Guard.ValidFileName(null, "Parameter"));
+ }
+
+ [Fact]
+ public void ValidFileName_should_do_nothing_for_valid_file_name()
+ {
+ Guard.ValidFileName("FileName", "Parameter");
+ }
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj
index d1144a582..14f6cb7a2 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/PinkParrot.Infrastructure.Tests.xproj
@@ -15,5 +15,8 @@
2.0
+
+
+
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs
new file mode 100644
index 000000000..c6ad001e9
--- /dev/null
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure.Tests/Reflection/SimpleMapperTests.cs
@@ -0,0 +1,83 @@
+// ==========================================================================
+// SimpleMapperTests.cs
+// PinkParrot Headless CMS
+// ==========================================================================
+// Copyright (c) PinkParrot Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+using Xunit;
+
+namespace PinkParrot.Infrastructure.Reflection
+{
+ public class SimpleMapperTests
+ {
+ public class Class1Base
+ {
+ public string MappedString { get; set; }
+
+ public string MappedNull { get; set; }
+
+ public long MappedNumber { get; set; }
+
+ public Guid MappedGuid { get; set; }
+ }
+
+ public class Class1 : Class1Base
+ {
+ public string UnmappedString { get; set; }
+ }
+
+ public class Class2Base
+ {
+ public string MappedString { get; protected set; }
+
+ public int MappedNull { get; set; }
+
+ public int MappedNumber { get; set; }
+
+ public string MappedGuid { get; set; }
+ }
+
+ public class Class2 : Class2Base
+ {
+ public string UnmappedString
+ {
+ get { return "Value"; }
+ }
+ }
+
+ [Fact]
+ public void Should_throw_if_mapping_with_null_source()
+ {
+ Assert.Throws(() => SimpleMapper.Map((Class1)null, new Class2()));
+ }
+
+ [Fact]
+ public void Should_throw_if_mapping_with_null_target()
+ {
+ Assert.Throws(() => SimpleMapper.Map(new Class1(), (Class2)null));
+ }
+
+ [Fact]
+ public void Should_map_between_types()
+ {
+ var class1 = new Class1
+ {
+ UnmappedString = Guid.NewGuid().ToString(),
+ MappedString = Guid.NewGuid().ToString(),
+ MappedNumber = 123,
+ MappedGuid = Guid.NewGuid()
+ };
+ var class2 = new Class2();
+
+ SimpleMapper.Map(class1, class2);
+
+ Assert.Equal(class1.MappedString, class2.MappedString);
+ Assert.Equal(class1.MappedNumber, class2.MappedNumber);
+ Assert.Equal(class1.MappedGuid.ToString(), class2.MappedGuid);
+ Assert.NotEqual(class1.UnmappedString, class2.UnmappedString);
+ }
+ }
+}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CollectionExtensions.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CollectionExtensions.cs
index 5c4e1bac4..dc47c0a3d 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CollectionExtensions.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/CollectionExtensions.cs
@@ -106,16 +106,5 @@ namespace PinkParrot.Infrastructure
return result;
}
-
- public static bool TryGetValueAsObject(this IReadOnlyDictionary dictionary, TKey key, out object value)
- {
- TValue result;
-
- var isFound = dictionary.TryGetValue(key, out result);
-
- value = result;
-
- return isFound;
- }
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Guard.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Guard.cs
index f999e5560..fb5cca769 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Guard.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/Guard.cs
@@ -56,11 +56,19 @@ namespace PinkParrot.Infrastructure
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasType(object target, string parameterName)
{
- NotNull(target, "parameterName");
+ if (target != null && target.GetType() != typeof(T))
+ {
+ throw new ArgumentException($"The parameter must be of type {typeof(T)}", parameterName);
+ }
+ }
- if (target.GetType() != typeof(T))
+ [DebuggerStepThrough]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void HasType(object target, Type expectedType, string parameterName)
+ {
+ if (target != null && expectedType != null && target.GetType() != expectedType)
{
- throw new ArgumentException("The parameter must be of type " + typeof(T), parameterName);
+ throw new ArgumentException($"The parameter must be of type {expectedType}", parameterName);
}
}
@@ -70,9 +78,7 @@ namespace PinkParrot.Infrastructure
{
if (!target.IsBetween(lower, upper))
{
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be between {0} and {1}", lower, upper);
-
- throw new ArgumentException(message, parameterName);
+ throw new ArgumentException($"Value must be between {lower} and {upper}", parameterName);
}
}
@@ -82,9 +88,7 @@ namespace PinkParrot.Infrastructure
{
if (!target.IsEnumValue())
{
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be a valid enum type {0}", typeof(TEnum));
-
- throw new ArgumentException(message, parameterName);
+ throw new ArgumentException($"Value must be a valid enum type {typeof(TEnum)}", parameterName);
}
}
@@ -94,9 +98,7 @@ namespace PinkParrot.Infrastructure
{
if (target.CompareTo(lower) <= 0)
{
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be greater than {0}", lower);
-
- throw new ArgumentException(message, parameterName);
+ throw new ArgumentException($"Value must be greater than {lower}", parameterName);
}
}
@@ -106,9 +108,7 @@ namespace PinkParrot.Infrastructure
{
if (target.CompareTo(lower) < 0)
{
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be greater than {0}", lower);
-
- throw new ArgumentException(message, parameterName);
+ throw new ArgumentException($"Value must be greater or equals than {lower}", parameterName);
}
}
@@ -118,9 +118,7 @@ namespace PinkParrot.Infrastructure
{
if (target.CompareTo(upper) >= 0)
{
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be less than {0}", upper);
-
- throw new ArgumentException(message, parameterName);
+ throw new ArgumentException($"Value must be less than {upper}", parameterName);
}
}
@@ -130,9 +128,7 @@ namespace PinkParrot.Infrastructure
{
if (target.CompareTo(upper) > 0)
{
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be less than {0}", upper);
-
- throw new ArgumentException(message, parameterName);
+ throw new ArgumentException($"Value must be less or equals than {upper}", parameterName);
}
}
@@ -140,10 +136,7 @@ namespace PinkParrot.Infrastructure
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void NotEmpty(ICollection enumerable, string parameterName)
{
- if (enumerable == null)
- {
- throw new ArgumentNullException(nameof(enumerable));
- }
+ NotNull(enumerable, parameterName);
if (enumerable.Count == 0)
{
@@ -183,22 +176,14 @@ namespace PinkParrot.Infrastructure
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void NotNullOrEmpty(string target, string parameterName, bool allowWhitespacesAtStartOrEnd = true)
+ public static void NotNullOrEmpty(string target, string parameterName)
{
- if (target == null)
- {
- throw new ArgumentNullException(parameterName);
- }
+ NotNull(target, parameterName);
if (string.IsNullOrWhiteSpace(target))
{
throw new ArgumentException("String parameter cannot be null or empty and cannot contain only blanks.", parameterName);
}
-
- if (!allowWhitespacesAtStartOrEnd && target.Trim() != target)
- {
- throw new ArgumentException("String cannot start or end with whitespaces", parameterName);
- }
}
[DebuggerStepThrough]
@@ -212,29 +197,5 @@ namespace PinkParrot.Infrastructure
throw new ArgumentException("Value contains an invalid character.", parameterName);
}
}
-
- [DebuggerStepThrough]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void IsType(object target, string parameterName)
- {
- if (target != null && target.GetType() != typeof(T))
- {
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be of type {0}", typeof(T));
-
- throw new ArgumentException(message, parameterName);
- }
- }
-
- [DebuggerStepThrough]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void IsType(object target, Type expectedType, string parameterName)
- {
- if (target != null && expectedType != null && target.GetType() != expectedType)
- {
- var message = string.Format(CultureInfo.CurrentCulture, "Value must be of type {0}", expectedType);
-
- throw new ArgumentException(message, parameterName);
- }
- }
}
}
diff --git a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/TypeNameRegistry.cs b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/TypeNameRegistry.cs
index 754cadf9a..928ebb50f 100644
--- a/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/TypeNameRegistry.cs
+++ b/src/pinkparrot_infrastructure/PinkParrot.Infrastructure/TypeNameRegistry.cs
@@ -24,9 +24,27 @@ namespace PinkParrot.Infrastructure
lock (namesByType)
{
- namesByType.Add(type, name);
+ try
+ {
+ namesByType.Add(type, name);
+ }
+ catch (ArgumentException)
+ {
+ var message = $"The type '{type}' is already registered with name '{namesByType[type]}'";
+
+ throw new ArgumentException(message, nameof(type));
+ }
- typesByName.Add(name, type);
+ try
+ {
+ typesByName.Add(name, type);
+ }
+ catch (ArgumentException)
+ {
+ var message = $"The name '{name}' is already registered with type '{typesByName[name]}'";
+
+ throw new ArgumentException(message, nameof(type));
+ }
}
}
diff --git a/src/pinkparrot_read/PinkParrot.Read/Models/FieldDto.cs b/src/pinkparrot_read/PinkParrot.Read/Models/FieldDto.cs
index f04ded3ed..c2db13356 100644
--- a/src/pinkparrot_read/PinkParrot.Read/Models/FieldDto.cs
+++ b/src/pinkparrot_read/PinkParrot.Read/Models/FieldDto.cs
@@ -6,22 +6,46 @@
// All rights reserved.
// ==========================================================================
-using Newtonsoft.Json;
using PinkParrot.Core.Schemas;
namespace PinkParrot.Read.Models
{
public class FieldDto
{
- [JsonProperty]
public string Name { get; set; }
- [JsonProperty]
- public IFieldProperties Properties { get; set; }
+ public bool IsHidden { get; set; }
+
+ public bool IsDisabled { get; set; }
+
+ public FieldProperties Properties { get; set; }
public static FieldDto Create(Field field)
{
- return new FieldDto { Name = field.Name, Properties = field.RawProperties };
+ return new FieldDto
+ {
+ Name = field.Name,
+ IsHidden = field.IsHidden,
+ IsDisabled = field.IsDisabled,
+ Properties = field.RawProperties
+ };
+ }
+
+ public Field ToField(long id, FieldRegistry registry)
+ {
+ var field = registry.CreateField(id, Name, Properties);
+
+ if (IsHidden)
+ {
+ field = field.Hide();
+ }
+
+ if (IsDisabled)
+ {
+ field = field.Disable();
+ }
+
+ return field;
}
}
}
\ No newline at end of file
diff --git a/src/pinkparrot_read/PinkParrot.Read/Models/SchemaDto.cs b/src/pinkparrot_read/PinkParrot.Read/Models/SchemaDto.cs
index c06edd658..6ad92e387 100644
--- a/src/pinkparrot_read/PinkParrot.Read/Models/SchemaDto.cs
+++ b/src/pinkparrot_read/PinkParrot.Read/Models/SchemaDto.cs
@@ -8,7 +8,6 @@
using System.Collections.Generic;
using System.Linq;
-using Newtonsoft.Json;
using PinkParrot.Core.Schemas;
using PinkParrot.Infrastructure;
@@ -19,13 +18,10 @@ namespace PinkParrot.Read.Models
{
public sealed class SchemaDto
{
- [JsonProperty]
public string Name { get; set; }
-
- [JsonProperty]
+
public Dictionary Fields { get; set; }
-
- [JsonProperty]
+
public SchemaProperties Properties { get; set; }
public static SchemaDto Create(Schema schema)
@@ -54,7 +50,7 @@ namespace PinkParrot.Read.Models
{
var field = kvp.Value;
- schema = schema.AddOrUpdateField(registry.CreateField(kvp.Key, field.Name, field.Properties));
+ schema = schema.AddOrUpdateField(field.ToField(kvp.Key, registry));
}
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaCommandHandler.cs b/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaCommandHandler.cs
index c99b0eadc..f8a728a14 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaCommandHandler.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaCommandHandler.cs
@@ -14,6 +14,7 @@ using PinkParrot.Core.Schemas;
using PinkParrot.Infrastructure;
using PinkParrot.Infrastructure.CQRS.Commands;
using PinkParrot.Infrastructure.Dispatching;
+using PinkParrot.Read.Services;
using PinkParrot.Write.Schemas.Commands;
namespace PinkParrot.Write.Schemas
@@ -21,18 +22,20 @@ namespace PinkParrot.Write.Schemas
public class SchemaCommandHandler : CommandHandler
{
private readonly FieldRegistry registry;
+ private readonly ISchemaProvider schemaProvider;
private readonly JsonSerializer serializer;
public SchemaCommandHandler(
FieldRegistry registry,
+ ISchemaProvider schemaProvider,
IDomainObjectFactory domainObjectFactory,
IDomainObjectRepository domainObjectRepository,
JsonSerializer serializer)
: base(domainObjectFactory, domainObjectRepository)
{
this.registry = registry;
-
this.serializer = serializer;
+ this.schemaProvider = schemaProvider;
}
public override Task HandleAsync(CommandContext context)
@@ -40,9 +43,15 @@ namespace PinkParrot.Write.Schemas
return context.IsHandled ? Task.FromResult(false) : this.DispatchActionAsync(context.Command);
}
- public Task On(CreateSchema command)
+ public async Task On(CreateSchema command)
{
- return CreateAsync(command, s => s.Create(command.TenantId, command.Name, command.Properties));
+ if (await schemaProvider.FindSchemaIdByNameAsync(command.TenantId, command.Name) != null)
+ {
+ var error = new ValidationError($"A schema with name '{command.Name}' already exists", "Name");
+
+ throw new ValidationException("Cannot create a new schema", error);
+ }
+ await CreateAsync(command, s => s.Create(command.TenantId, command.Name, command.Properties));
}
public Task On(DeleteSchema command)
@@ -106,9 +115,9 @@ namespace PinkParrot.Write.Schemas
});
}
- private IFieldProperties CreateProperties(JToken token, Type type)
+ private FieldProperties CreateProperties(JToken token, Type type)
{
- return (IFieldProperties)token.ToObject(type, serializer);
+ return (FieldProperties)token.ToObject(type, serializer);
}
}
}
diff --git a/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaDomainObject.cs b/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaDomainObject.cs
index 9c3b12094..3dd286354 100644
--- a/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaDomainObject.cs
+++ b/src/pinkparrot_write/PinkParrot.Write/Schemas/SchemaDomainObject.cs
@@ -101,7 +101,7 @@ namespace PinkParrot.Write.Schemas
isDeleted = false;
}
- public void AddField(string name, IFieldProperties properties)
+ public void AddField(string name, FieldProperties properties)
{
VerifyCreatedAndNotDeleted();
@@ -122,7 +122,7 @@ namespace PinkParrot.Write.Schemas
RaiseEvent(new SchemaUpdated { Properties = properties });
}
- public void UpdateField(long fieldId, IFieldProperties properties)
+ public void UpdateField(long fieldId, FieldProperties properties)
{
VerifyCreatedAndNotDeleted();