mirror of https://github.com/Squidex/squidex.git
36 changed files with 462 additions and 134 deletions
@ -0,0 +1,51 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure.Queries; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators |
||||
|
{ |
||||
|
public sealed class UniqueValidator : IValidator |
||||
|
{ |
||||
|
public async Task ValidateAsync(object value, ValidationContext context, AddError addError) |
||||
|
{ |
||||
|
var count = context.Path.Count(); |
||||
|
|
||||
|
if (value != null && (count == 0 || (count == 2 && context.Path.Last() == InvariantPartitioning.Instance.Master.Key))) |
||||
|
{ |
||||
|
FilterNode filter = null; |
||||
|
|
||||
|
if (value is string s) |
||||
|
{ |
||||
|
filter = new FilterComparison(Path(context), FilterOperator.Equals, new FilterValue(s)); |
||||
|
} |
||||
|
else if (value is double d) |
||||
|
{ |
||||
|
filter = new FilterComparison(Path(context), FilterOperator.Equals, new FilterValue(d)); |
||||
|
} |
||||
|
|
||||
|
if (filter != null) |
||||
|
{ |
||||
|
var found = await context.GetContentIdsAsync(context.SchemaId, filter); |
||||
|
|
||||
|
if (found.Any(x => x != context.ContentId)) |
||||
|
{ |
||||
|
addError(context.Path, "Another content with the same value exists."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static List<string> Path(ValidationContext context) |
||||
|
{ |
||||
|
return Enumerable.Repeat("Data", 1).Union(context.Path).ToList(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using NodaTime; |
||||
|
using Squidex.Infrastructure.Queries; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors |
||||
|
{ |
||||
|
internal sealed class AdaptionVisitor : TransformVisitor |
||||
|
{ |
||||
|
private readonly Func<IReadOnlyList<string>, IReadOnlyList<string>> pathConverter; |
||||
|
|
||||
|
public AdaptionVisitor(Func<IReadOnlyList<string>, IReadOnlyList<string>> pathConverter) |
||||
|
{ |
||||
|
this.pathConverter = pathConverter; |
||||
|
} |
||||
|
|
||||
|
public override FilterNode Visit(FilterComparison nodeIn) |
||||
|
{ |
||||
|
FilterComparison result; |
||||
|
|
||||
|
var value = nodeIn.Rhs.Value; |
||||
|
|
||||
|
if (value is Instant && |
||||
|
!string.Equals(nodeIn.Lhs[0], "mt", StringComparison.OrdinalIgnoreCase) && |
||||
|
!string.Equals(nodeIn.Lhs[0], "ct", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
result = new FilterComparison(pathConverter(nodeIn.Lhs), nodeIn.Operator, new FilterValue(value.ToString())); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = new FilterComparison(pathConverter(nodeIn.Lhs), nodeIn.Operator, nodeIn.Rhs); |
||||
|
} |
||||
|
|
||||
|
if (result.Lhs.Count == 1 && result.Lhs[0] == "_id" && result.Rhs.Value is List<Guid> guidList) |
||||
|
{ |
||||
|
result = new FilterComparison(nodeIn.Lhs, nodeIn.Operator, new FilterValue(guidList.Select(x => x.ToString()).ToList())); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using FluentAssertions; |
||||
|
using Squidex.Domain.Apps.Core.ValidateContent; |
||||
|
using Squidex.Domain.Apps.Core.ValidateContent.Validators; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators |
||||
|
{ |
||||
|
public class UniqueValidatorTests |
||||
|
{ |
||||
|
private readonly List<string> errors = new List<string>(); |
||||
|
private readonly Guid contentId = Guid.NewGuid(); |
||||
|
private readonly Guid schemaId = Guid.NewGuid(); |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_string_value_not_found() |
||||
|
{ |
||||
|
var sut = new UniqueValidator(); |
||||
|
|
||||
|
var filter = string.Empty; |
||||
|
|
||||
|
await sut.ValidateAsync("hi", errors, Context(Guid.NewGuid(), f => filter = f)); |
||||
|
|
||||
|
errors.Should().BeEquivalentTo( |
||||
|
new[] { "property: Another content with the same value exists." }); |
||||
|
|
||||
|
Assert.Equal("Data.property.iv == 'hi'", filter); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_add_error_if_double_value_not_found() |
||||
|
{ |
||||
|
var sut = new UniqueValidator(); |
||||
|
|
||||
|
var filter = string.Empty; |
||||
|
|
||||
|
await sut.ValidateAsync(12.5, errors, Context(Guid.NewGuid(), f => filter = f)); |
||||
|
|
||||
|
errors.Should().BeEquivalentTo( |
||||
|
new[] { "property: Another content with the same value exists." }); |
||||
|
|
||||
|
Assert.Equal("Data.property.iv == 12.5", filter); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_string_value_found() |
||||
|
{ |
||||
|
var sut = new UniqueValidator(); |
||||
|
|
||||
|
var filter = string.Empty; |
||||
|
|
||||
|
await sut.ValidateAsync("hi", errors, Context(contentId, f => filter = f)); |
||||
|
|
||||
|
Assert.Empty(errors); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_add_error_if_double_value_found() |
||||
|
{ |
||||
|
var sut = new UniqueValidator(); |
||||
|
|
||||
|
var filter = string.Empty; |
||||
|
|
||||
|
await sut.ValidateAsync(12.5, errors, Context(contentId, f => filter = f)); |
||||
|
|
||||
|
Assert.Empty(errors); |
||||
|
} |
||||
|
|
||||
|
private ValidationContext Context(Guid id, Action<string> filter) |
||||
|
{ |
||||
|
return new ValidationContext(contentId, schemaId, |
||||
|
(schema, filterNode) => |
||||
|
{ |
||||
|
filter(filterNode.ToString()); |
||||
|
|
||||
|
return Task.FromResult<IReadOnlyList<Guid>>(new List<Guid> { id }); |
||||
|
}, |
||||
|
ids => |
||||
|
{ |
||||
|
return Task.FromResult<IReadOnlyList<IAssetInfo>>(new List<IAssetInfo>()); |
||||
|
}).Nested("property").Nested("iv"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue