unique validator

This commit is contained in:
2020-09-09 11:02:22 +02:00
parent a8ed36ddd1
commit 801d801eb8
8 changed files with 95 additions and 57 deletions
@@ -0,0 +1,55 @@
using Raven.Client.Documents.Session;
using System;
using System.Collections.Generic;
using zero.Core.Api;
using zero.Core.Entities;
namespace zero.Core.Extensions
{
public static class RavenAsyncDocumentQueryExtensions
{
static Type _appAwareEntity = typeof(IAppAwareEntity);
public static IAsyncDocumentQuery<T> Scope<T>(this IAsyncDocumentQuery<T> source, string appId, bool includeShared = true)
{
if (appId.IsNullOrEmpty() || !_appAwareEntity.IsAssignableFrom(typeof(T)))
{
return source;
}
HashSet<string> ids = new HashSet<string>();
ids.Add(appId);
if (includeShared)
{
ids.Add(Constants.Database.SharedAppId);
}
return source.WhereIn(nameof(IAppAwareEntity.AppId), ids);
}
public static IAsyncDocumentQuery<T> Scope<T>(this IAsyncDocumentQuery<T> source, ApiScope scope)
{
if (scope == null || scope.IsShared)
{
return source;
}
if (scope.AppId.IsNullOrEmpty() || !_appAwareEntity.IsAssignableFrom(typeof(T)))
{
return source;
}
HashSet<string> ids = new HashSet<string>();
ids.Add(scope.AppId);
if (scope.IncludeShared)
{
ids.Add(Constants.Database.SharedAppId);
}
return source.WhereIn(nameof(IAppAwareEntity.AppId), ids);
}
}
}
+27
View File
@@ -89,6 +89,15 @@ namespace zero.Core.Extensions
return input;
}
public static string ToPascalCase(this string input)
{
if (!String.IsNullOrEmpty(input) && input.Length > 1)
{
return Char.ToUpperInvariant(input[0]) + input.Substring(1);
}
return input;
}
public static string ToCamelCaseId(this string input)
{
if (String.IsNullOrEmpty(input))
@@ -105,5 +114,23 @@ namespace zero.Core.Extensions
return String.Join(".", parts.Select(x => x.ToCamelCase()));
}
public static string ToPascalCaseId(this string input)
{
if (String.IsNullOrEmpty(input))
{
return input;
}
if (input.Length < 2)
{
return input.ToUpperInvariant();
}
string[] parts = input.Split('.');
return String.Join(".", parts.Select(x => x.ToPascalCase()));
}
}
}
+9 -7
View File
@@ -100,19 +100,21 @@ namespace zero.Core.Extensions
/// <summary>
/// Check if this value is unique within a collection
/// </summary>
public static IRuleBuilderOptions<T, object> Unique<T>(this IRuleBuilder<T, object> ruleBuilder, IBackofficeStore store, bool includeShared = true) where T : IZeroIdEntity
public static IRuleBuilderOptions<T, object> Unique<T>(this IRuleBuilder<T, object> ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity
{
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
{
bool includeShared = typeof(IAppAwareShareableEntity).IsAssignableFrom(typeof(T));
using IAsyncDocumentSession session = store.Raven.OpenAsyncSession();
int count = await session.Advanced.AsyncDocumentQuery<T>()
.WhereNotEquals("Id", ((T)entity).Id, true)
.WhereEquals(context.Rule.PropertyName, value, true)
.CountAsync();
bool any = await session.Advanced.AsyncDocumentQuery<T>()
.Scope(store.AppContext.AppId, includeShared)
.WhereNotEquals(nameof(IZeroIdEntity.Id), entity.Id)
.WhereEquals(context.Rule.PropertyName.ToPascalCaseId(), value)
.AnyAsync(cancellation);
return count < 1;
//.Scope(store.AppContext.AppId, includeShared)
return !any;
}).WithMessage("@errors.forms.not_unique");
}
}
+2 -18
View File
@@ -2,30 +2,14 @@
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Extensions;
using Raven.Client.Documents.Session;
using Raven.Client.Documents;
namespace zero.Core.Validation
{
public class CountryValidator : ZeroValidator<ICountry, Country>
{
public CountryValidator(IBackofficeStore store) : base(store)
public CountryValidator(IBackofficeStore store)
{
RuleFor(x => x.Code).Length(2);
RuleFor(x => x.Code).Unique(store);
//RuleFor(x => x.Code).Query<ICountry, Country>(store, async (query, entity) =>
//{
// return !(await query.Scope(store.AppContext.AppId, true).AnyAsync(x => x.Id != entity.Id && x.Code == value));
//});
//RuleFor(x => x.Code).MustAsync(async (entity, value, ct) =>
//{
// using IAsyncDocumentSession session = Raven.OpenAsyncSession();
// return !(await session.Query<ICountry>().Scope(store.AppContext.AppId, true).AnyAsync(x => x.Id != entity.Id && x.Code == value));
//});
RuleFor(x => x.Code).Length(2).Unique(store);
RuleFor(x => x.Name).Length(2, 120);
}
}
@@ -1,20 +0,0 @@
using FluentValidation.Validators;
using System;
using System.Collections.Generic;
using System.Text;
namespace zero.Core.Validation
{
public class UniqueValidator : PropertyValidator
{
public UniqueValidator() : base("@errors.forms.not_unique")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
throw new NotImplementedException();
}
}
}
-11
View File
@@ -10,17 +10,6 @@ namespace zero.Core.Validation
{
public abstract class ZeroValidator<TInterface, TImplementation> : AbstractValidator<TImplementation>, IValidator<TInterface> where TImplementation : TInterface
{
protected IBackofficeStore Store { get; private set; }
protected IDocumentStore Raven { get; private set; }
public ZeroValidator(IBackofficeStore store)
{
Store = store;
Raven = store.Raven;
}
public ValidationResult Validate(TInterface instance)
{
if (!(instance is TImplementation))
@@ -169,6 +169,7 @@
{
this.setState('success');
this.setDirty(false);
this.setErrors(null);
resolve(response);
if (response.model && this.route && this.$route.name !== this.route)
@@ -167,7 +167,7 @@
"emails_invalid": "The email address(es) is/are not valid",
"hex_format": "The input is not in a valid HEX #aabbcc format",
"url_format": "The input is no well-formed URL",
"not_unique": "The property \"{PropertyName}\" must be unique in this collection"
"not_unique": "The property must be unique in this collection"
}
},