Browse Source

Merge branch 'refactoring/patterns'

pull/370/head
Sebastian 7 years ago
parent
commit
aeb855fa57
  1. 6
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs
  2. 15
      src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs
  3. 20
      src/Squidex.Domain.Apps.Entities/Apps/AppExtensions.cs
  4. 4
      src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs
  5. 3
      src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlanBillingManager.cs
  6. 3
      src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/NoopAppPlanBillingManager.cs
  7. 21
      src/Squidex.Domain.Apps.Entities/EntityExtensions.cs
  8. 84
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs
  9. 8
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs
  10. 2
      src/Squidex.Web/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs
  11. 2
      src/Squidex.Web/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs
  12. 4
      src/Squidex/app/shared/state/contents.forms.ts
  13. 37
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs
  14. 12
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs
  15. 3
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Billing/NoopAppPlanBillingManagerTests.cs
  16. 2
      tests/Squidex.Domain.Apps.Entities.Tests/History/Notifications/NotificationEmailEventConsumerTests.cs
  17. 4
      tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs

6
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs

@ -34,11 +34,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
var typedValue = value;
if (value == null)
{
typedValue = Undefined.Value;
}
else if (value is IJsonValue jsonValue)
if (value is IJsonValue jsonValue)
{
if (jsonValue.Type == JsonValueType.Null)
{

15
src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs

@ -26,7 +26,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
public async Task ValidateAsync(object value, ValidationContext context, AddError addError)
{
if (value == null)
if (value.IsNullOrUndefined())
{
value = DefaultValue;
}
@ -49,17 +49,22 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
var name = field.Key;
if (!values.TryGetValue(name, out var fieldValue))
var (isOptional, validator) = field.Value;
var fieldValue = Undefined.Value;
if (!values.TryGetValue(name, out var temp))
{
if (isPartial)
{
continue;
}
fieldValue = default;
}
else
{
fieldValue = temp;
}
var (isOptional, validator) = field.Value;
var fieldContext = context.Nested(name).Optional(isOptional);
tasks.Add(validator.ValidateAsync(fieldValue, fieldContext, addError));

20
src/Squidex.Domain.Apps.Entities/Apps/AppExtensions.cs

@ -0,0 +1,20 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps
{
public static class AppExtensions
{
public static NamedId<Guid> NamedId(this IAppEntity app)
{
return new NamedId<Guid>(app.Id, app.Name);
}
}
}

4
src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs

@ -194,7 +194,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
}
else
{
var result = await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.Id, Snapshot.Name, c.PlanId);
var result = await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.NamedId(), c.PlanId);
switch (result)
{
@ -213,7 +213,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
case ArchiveApp archiveApp:
return UpdateAsync(archiveApp, async c =>
{
await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.Id, Snapshot.Name, null);
await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.NamedId(), null);
ArchiveApp(c);
});

3
src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlanBillingManager.cs

@ -7,6 +7,7 @@
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
@ -14,7 +15,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Services
{
bool HasPortal { get; }
Task<IChangePlanResult> ChangePlanAsync(string userId, Guid appId, string appName, string planId);
Task<IChangePlanResult> ChangePlanAsync(string userId, NamedId<Guid> appId, string planId);
Task<string> GetPortalLinkAsync(string userId);
}

3
src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/NoopAppPlanBillingManager.cs

@ -7,6 +7,7 @@
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations
{
@ -17,7 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations
get { return false; }
}
public Task<IChangePlanResult> ChangePlanAsync(string userId, Guid appId, string appName, string planId)
public Task<IChangePlanResult> ChangePlanAsync(string userId, NamedId<Guid> appId, string planId)
{
return Task.FromResult<IChangePlanResult>(new PlanResetResult());
}

21
src/Squidex.Domain.Apps.Entities/EntityExtensions.cs

@ -0,0 +1,21 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities
{
public static class EntityExtensions
{
public static NamedId<Guid> NamedId(this IAppEntity entity)
{
return new NamedId<Guid>(entity.Id, entity.Name);
}
}
}

84
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs

@ -142,49 +142,73 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
fieldIndex++;
fieldPrefix = $"Fields[{fieldIndex}]";
if (!field.Partitioning.IsValidPartitioning())
{
e(Not.Valid("Partitioning"), $"{fieldPrefix}.{nameof(field.Partitioning)}");
}
ValidateRootField(field, fieldPrefix, e);
}
ValidateField(field, fieldPrefix, e);
if (command.Fields.Select(x => x?.Name).Distinct().Count() != command.Fields.Count)
{
e("Fields cannot have duplicate names.", nameof(command.Fields));
}
}
}
if (field.Nested?.Count > 0)
{
if (field.Properties is ArrayFieldProperties)
{
var nestedIndex = 0;
var nestedPrefix = string.Empty;
private static void ValidateRootField(UpsertSchemaField field, string prefix, AddValidation e)
{
if (field == null)
{
e(Not.Defined("Field"), prefix);
}
else
{
if (!field.Partitioning.IsValidPartitioning())
{
e(Not.Valid("Partitioning"), $"{prefix}.{nameof(field.Partitioning)}");
}
foreach (var nestedField in field.Nested)
{
nestedIndex++;
nestedPrefix = $"{fieldPrefix}.Nested[{nestedIndex}]";
ValidateField(field, prefix, e);
if (nestedField.Properties is ArrayFieldProperties)
{
e("Nested field cannot be array fields.", $"{nestedPrefix}.{nameof(nestedField.Properties)}");
}
if (field.Nested?.Count > 0)
{
if (field.Properties is ArrayFieldProperties)
{
var nestedIndex = 0;
var nestedPrefix = string.Empty;
ValidateField(nestedField, nestedPrefix, e);
}
}
else if (field.Nested.Count > 0)
foreach (var nestedField in field.Nested)
{
e("Only array fields can have nested fields.", $"{fieldPrefix}.{nameof(field.Partitioning)}");
}
nestedIndex++;
nestedPrefix = $"{prefix}.Nested[{nestedIndex}]";
if (field.Nested.Select(x => x.Name).Distinct().Count() != field.Nested.Count)
{
e("Fields cannot have duplicate names.", $"{fieldPrefix}.Nested");
ValidateNestedField(nestedField, nestedPrefix, e);
}
}
else if (field.Nested.Count > 0)
{
e("Only array fields can have nested fields.", $"{prefix}.{nameof(field.Partitioning)}");
}
if (field.Nested.Select(x => x.Name).Distinct().Count() != field.Nested.Count)
{
e("Fields cannot have duplicate names.", $"{prefix}.Nested");
}
}
}
}
if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count)
private static void ValidateNestedField(UpsertSchemaNestedField nestedField, string prefix, AddValidation e)
{
if (nestedField == null)
{
e(Not.Defined("Field"), prefix);
}
else
{
if (nestedField.Properties is ArrayFieldProperties)
{
e("Fields cannot have duplicate names.", nameof(command.Fields));
e("Nested field cannot be array fields.", $"{prefix}.{nameof(nestedField.Properties)}");
}
ValidateField(nestedField, prefix, e);
}
}

8
src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs

@ -321,7 +321,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
if (id.HasValue && Snapshot.SchemaDef.FieldsById.TryGetValue(id.Value, out var field))
{
return NamedId.Of(field.Id, field.Name);
return field.NamedId();
}
return null;
@ -333,13 +333,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
if (Snapshot.SchemaDef.FieldsById.TryGetValue(pc.ParentFieldId.Value, out var field))
{
pe.ParentFieldId = NamedId.Of(field.Id, field.Name);
pe.ParentFieldId = field.NamedId();
if (command is FieldCommand fc && @event is FieldEvent fe)
{
if (field is IArrayField arrayField && arrayField.FieldsById.TryGetValue(fc.FieldId, out var nestedField))
{
fe.FieldId = NamedId.Of(nestedField.Id, nestedField.Name);
fe.FieldId = nestedField.NamedId();
}
}
}
@ -357,7 +357,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
if (@event.SchemaId == null)
{
@event.SchemaId = NamedId.Of(Snapshot.Id, Snapshot.SchemaDef.Name);
@event.SchemaId = Snapshot.NamedId();
}
if (@event.AppId == null)

2
src/Squidex.Web/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs

@ -57,7 +57,7 @@ namespace Squidex.Web.CommandMiddlewares
throw new InvalidOperationException("Cannot resolve app.");
}
return NamedId.Of(appFeature.App.Id, appFeature.App.Name);
return appFeature.App.NamedId();
}
}
}

2
src/Squidex.Web/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs

@ -69,7 +69,7 @@ namespace Squidex.Web.CommandMiddlewares
if (appFeature?.App != null)
{
appId = NamedId.Of(appFeature.App.Id, appFeature.App.Name);
appId = appFeature.App.NamedId();
}
}

4
src/Squidex/app/shared/state/contents.forms.ts

@ -331,7 +331,7 @@ export class EditContentForm extends Form<FormGroup, any> {
super(new FormGroup({}));
for (const field of schema.fields) {
if (field.properties.fieldType !== 'UI') {
if (field.properties.isContentField) {
const fieldForm = new FormGroup({});
const fieldDefault = FieldDefaultValue.get(field);
@ -378,7 +378,7 @@ export class EditContentForm extends Form<FormGroup, any> {
let isOptional = field.isLocalizable && !!language && language.isOptional;
for (let nested of field.nested) {
if (nested.properties.fieldType !== 'UI') {
if (nested.properties.isContentField) {
const nestedValidators = FieldValidatorsFactory.createValidators(nested, isOptional);
let value = FieldDefaultValue.get(nested);

37
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs

@ -339,9 +339,8 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact]
public async Task Should_add_error_if_array_field_has_required_nested_field()
{
schema =
schema.AddArray(1, "my-field", Partitioning.Invariant, f => f.
AddNumber(1, "my-nested", new NumberFieldProperties { IsRequired = true }));
schema = schema.AddArray(1, "my-field", Partitioning.Invariant, f => f.
AddNumber(2, "my-nested", new NumberFieldProperties { IsRequired = true }));
var data =
new NamedContentData()
@ -362,5 +361,37 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
new ValidationError("Field is required.", "my-field[3].my-nested")
});
}
[Fact]
public async Task Should_not_add_error_if_separator_not_defined()
{
schema = schema.AddUI(2, "ui", Partitioning.Invariant);
var data =
new NamedContentData();
await data.ValidateAsync(context, schema, languagesConfig.ToResolver(), errors);
Assert.Empty(errors);
}
[Fact]
public async Task Should_not_add_error_if_nested_separator_not_defined()
{
schema = schema.AddArray(1, "my-field", Partitioning.Invariant, f => f.
AddUI(2, "my-nested"));
var data =
new NamedContentData()
.AddField("my-field",
new ContentFieldData()
.AddValue("iv",
JsonValue.Array(
JsonValue.Object())));
await data.ValidateAsync(context, schema, languagesConfig.ToResolver(), errors);
Assert.Empty(errors);
}
}
}

12
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs

@ -103,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
var command = new ChangePlan { PlanId = planIdPaid };
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
.Returns(new PlanChangedResult());
await ExecuteCreateAsync();
@ -125,10 +125,10 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
var command = new ChangePlan { PlanId = planIdFree };
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
.Returns(new PlanChangedResult());
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planIdFree))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdFree))
.Returns(new PlanResetResult());
await ExecuteCreateAsync();
@ -151,7 +151,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
var command = new ChangePlan { PlanId = planIdPaid };
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
.Returns(new RedirectToCheckoutResult(new Uri("http://squidex.io")));
await ExecuteCreateAsync();
@ -174,7 +174,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
result.ShouldBeEquivalent(new EntitySavedResult(5));
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
.MustNotHaveHappened();
}
@ -486,7 +486,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
CreateEvent(new AppArchived())
);
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, AppId, AppName, null))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, AppNamedId, null))
.MustHaveHappened();
}

3
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Billing/NoopAppPlanBillingManagerTests.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Apps.Services.Implementations;
using Xunit;
@ -25,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Billing
[Fact]
public async Task Should_do_nothing_when_changing_plan()
{
await sut.ChangePlanAsync(null, Guid.Empty, null, null);
await sut.ChangePlanAsync(null, null, null);
}
[Fact]

2
tests/Squidex.Domain.Apps.Entities.Tests/History/Notifications/NotificationEmailEventConsumerTests.cs

@ -174,7 +174,7 @@ namespace Squidex.Domain.Apps.Entities.History.Notifications
var @event = new AppContributorAssigned
{
Actor = new RefToken(assignerType, assignerId),
AppId = new NamedId<Guid>(Guid.NewGuid(), appName),
AppId = NamedId.Of(Guid.NewGuid(), appName),
ContributorId = assigneeId,
IsCreated = isNewUser,
IsAdded = isNewContributor,

4
tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs

@ -81,7 +81,7 @@ namespace Squidex.Web.CommandMiddlewares
[Fact]
public async Task Should_assign_app_id_to_app_self_command()
{
var command = new AddPattern();
var command = new ChangePlan();
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
@ -92,7 +92,7 @@ namespace Squidex.Web.CommandMiddlewares
[Fact]
public async Task Should_not_override_app_id()
{
var command = new AddPattern { AppId = Guid.NewGuid() };
var command = new ChangePlan { AppId = Guid.NewGuid() };
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);

Loading…
Cancel
Save