validation for languages+countries+translations+mails
This commit is contained in:
@@ -12,8 +12,8 @@
|
||||
|
||||
<div class="ui-property-content">
|
||||
<slot></slot>
|
||||
<ui-error v-if="field" :field="field" />
|
||||
</div>
|
||||
<ui-error v-if="field" :field="field" />
|
||||
<slot name="after"></slot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -64,9 +64,9 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.ui-error
|
||||
&:not(.is-vertical) > .ui-error
|
||||
{
|
||||
margin-top: 5px;
|
||||
grid-column: span 2 / auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,13 @@ public class ZeroIndexController : Controller
|
||||
return RedirectToAction("ZeroBackoffice", "Setup");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//return View("~/Views/Zero/Index.cshtml", new ZeroBackofficeModel()
|
||||
//{
|
||||
// Meta = GetMetaInfo()
|
||||
//});
|
||||
|
||||
BackofficeOptions options = Options.For<BackofficeOptions>();
|
||||
|
||||
int port = options.DevServer.Port;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model zero.Web.Models.ZeroBackofficeModel
|
||||
@{
|
||||
Layout = null;
|
||||
string config = await Model.Vue.ConfigAsJson();
|
||||
}
|
||||
@model zero.Backoffice.ZeroBackofficeModel
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -25,8 +22,9 @@
|
||||
<title>zero</title>
|
||||
</head>
|
||||
<body class="theme-light theme-rounded">
|
||||
@Model.Meta.AppLastModifiedDate
|
||||
<div id="app"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
@* <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
@Html.Raw(Model.Vue.GetIconSvg())
|
||||
</svg>
|
||||
<script id="zeroconfig" type="application/json">
|
||||
@@ -39,6 +37,6 @@
|
||||
<environment include="Development">
|
||||
<script type="module" src="http://localhost:@(Model.Port)/vite/client"></script>
|
||||
<script type="module" src="http://localhost:@(Model.Port)/app/app.js"></script>
|
||||
</environment>
|
||||
</environment>*@
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
@using zero.Core.Extensions
|
||||
@*@using zero.Core.Extensions
|
||||
@inject zero.Core.Options.IZeroOptions Options
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@{
|
||||
@@ -27,4 +27,4 @@
|
||||
</script>
|
||||
<script src="~/Assets/setup.js" asp-append-version="true"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>*@
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace zero.Backoffice;
|
||||
|
||||
internal class ZeroBackofficeModel
|
||||
{
|
||||
public ZeroBackofficeMetaInfo Meta { get; set; }
|
||||
}
|
||||
|
||||
|
||||
internal class ZeroVueManifest
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class ZeroVueManifestFile
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>zero.Backoffice</PackageId>
|
||||
<Version>0.1.0</Version>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -17,17 +18,17 @@
|
||||
<None Remove="Modules_legacy\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--<ItemGroup>
|
||||
<None Remove="Resources\Countries\countries.de-de.json" />
|
||||
<None Remove="Resources\Countries\countries.en-us.json" />
|
||||
<None Remove="Resources\Localization\zero.de-de.json" />
|
||||
<None Remove="Resources\Localization\zero.en-us.json" />
|
||||
</ItemGroup>
|
||||
</ItemGroup>-->
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\backoffice.tpl.html" />
|
||||
|
||||
<Content Include="Resources\Countries\countries.de-de.json">
|
||||
<!--<Content Include="Resources\Countries\countries.de-de.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Resources\Countries\countries.en-us.json">
|
||||
@@ -38,7 +39,7 @@
|
||||
</Content>
|
||||
<Content Include="Resources\Localization\zero.en-us.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</Content>-->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -145,7 +145,7 @@ public class IntegrationStore : IIntegrationStore
|
||||
return Result<Integration>.Fail("@integration.errors.alreadycreated");
|
||||
}
|
||||
|
||||
return await Operations.Create(model, async x => await Validate(x));
|
||||
return await Operations.Create(model, async (x, ctx) => await Validate(x));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using FluentValidation.Results;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Raven.Client.Documents;
|
||||
using Raven.Client.Documents.Linq;
|
||||
using System.Security.Claims;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace zero.Localization;
|
||||
|
||||
@@ -6,11 +6,10 @@ public class CountryStore : EntityStore<Country>, ICountryStore
|
||||
{
|
||||
public CountryStore(IStoreContext context) : base(context) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ValidationRules(ZeroValidator<Country> validator)
|
||||
|
||||
public override Task<ValidationResult> Validate(ZeroValidationContext ctx, Country model)
|
||||
{
|
||||
validator.RuleFor(x => x.Code).NotEmpty().Length(2).Unique(Session);
|
||||
validator.RuleFor(x => x.Name).NotEmpty().Length(2, 120);
|
||||
return new CountryValidator(ctx).ValidateAsync(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace zero.Localization;
|
||||
|
||||
public class CountryValidator : ZeroValidator<Country>
|
||||
{
|
||||
public CountryValidator(ZeroValidationContext ctx)
|
||||
{
|
||||
RuleFor(x => x.Code).NotEmpty().Length(2).Unique(ctx);
|
||||
RuleFor(x => x.Name).NotEmpty().Length(2, 120);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace zero.Localization;
|
||||
|
||||
@@ -6,17 +6,10 @@ public class LanguageStore : EntityStore<Language>, ILanguageStore
|
||||
{
|
||||
public LanguageStore(IStoreContext context) : base(context) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ValidationRules(ZeroValidator<Language> validator)
|
||||
|
||||
public override Task<ValidationResult> Validate(ZeroValidationContext ctx, Language model)
|
||||
{
|
||||
validator.RuleFor(x => x.Name).Length(2, 60);
|
||||
validator.RuleFor(x => x.Code).Length(2, 10).Culture();
|
||||
validator.RuleFor(x => x.IsDefault).Unique(Context.Store).When(x => x.IsDefault).WithMessage("@language.errors.default_unique");
|
||||
validator.RuleFor(x => x.IsDefault).ExpectAnyUnique(Context.Store, expectedValue: true).When(x => !x.IsDefault).WithMessage("@language.errors.needs_default");
|
||||
validator.RuleFor(x => x.InheritedLanguageId).Must((entity, value) => !entity.Id.Equals(value, StringComparison.InvariantCultureIgnoreCase)).When(x => !x.Id.IsNullOrEmpty()).WithMessage("@language.errors.fallback_invalid");
|
||||
validator.RuleFor(x => x.InheritedLanguageId).Equal((string)null).When(x => x.IsDefault).WithMessage("@language.errors.default_no_fallback");
|
||||
validator.RuleFor(x => x.InheritedLanguageId).Exists(Context.Store);
|
||||
validator.RuleFor(x => x.IsOptional).Equal(false).When(x => x.IsDefault).WithMessage("@language.errors.default_not_optional");
|
||||
return new LanguageValidator(ctx).ValidateAsync(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace zero.Localization;
|
||||
|
||||
public class LanguageValidator : ZeroValidator<Language>
|
||||
{
|
||||
public LanguageValidator(ZeroValidationContext ctx)
|
||||
{
|
||||
RuleFor(x => x.Name).Length(2, 60);
|
||||
RuleFor(x => x.Code).Length(2, 10).Culture();
|
||||
RuleFor(x => x.IsDefault).Unique(ctx).When(x => x.IsDefault).WithMessage("@language.errors.default_unique");
|
||||
RuleFor(x => x.IsDefault).ExpectAnyUnique(ctx, expectedValue: true).When(x => !x.IsDefault).WithMessage("@language.errors.needs_default");
|
||||
RuleFor(x => x.InheritedLanguageId).Must((entity, value) => !entity.Id.Equals(value, StringComparison.InvariantCultureIgnoreCase)).When(x => !x.Id.IsNullOrEmpty()).WithMessage("@language.errors.fallback_invalid");
|
||||
RuleFor(x => x.InheritedLanguageId).Equal((string)null).When(x => x.IsDefault).WithMessage("@language.errors.default_no_fallback");
|
||||
RuleFor(x => x.InheritedLanguageId).Exists(ctx);
|
||||
RuleFor(x => x.IsOptional).Equal(false).When(x => x.IsDefault).WithMessage("@language.errors.default_not_optional");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace zero.Localization;
|
||||
|
||||
@@ -14,10 +14,9 @@ public class TranslationStore : EntityStore<Translation>, ITranslationStore
|
||||
}
|
||||
|
||||
|
||||
protected override void ValidationRules(ZeroValidator<Translation> validator)
|
||||
public override Task<ValidationResult> Validate(ZeroValidationContext ctx, Translation model)
|
||||
{
|
||||
validator.RuleFor(x => x.Key).NotEmpty().Length(2, 300).Unique(Context.Store);
|
||||
validator.RuleFor(x => x.Value).MaximumLength(10 * 1000);
|
||||
return new TranslationValidator(ctx).ValidateAsync(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace zero.Localization;
|
||||
|
||||
public class TranslationValidator : ZeroValidator<Translation>
|
||||
{
|
||||
public TranslationValidator(ZeroValidationContext ctx)
|
||||
{
|
||||
RuleFor(x => x.Key).NotEmpty().Length(2, 300).Unique(ctx);
|
||||
RuleFor(x => x.Value).MaximumLength(10 * 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace zero.Mails;
|
||||
|
||||
public class MailTemplateValidator : ZeroValidator<MailTemplate>
|
||||
{
|
||||
public MailTemplateValidator(ZeroValidationContext ctx, IMailDispatcher dispatcher)
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().Length(2, 80);
|
||||
RuleFor(x => x.Key).NotEmpty().Unique(ctx);
|
||||
RuleFor(x => x.Subject).NotEmpty().Length(2, 120);
|
||||
RuleFor(x => x.Preheader).MaximumLength(240);
|
||||
|
||||
RuleFor(x => x.SenderEmail).Email();
|
||||
RuleFor(x => x.SenderName).MaximumLength(120);
|
||||
|
||||
RuleFor(x => x.RecipientEmail).Emails();
|
||||
RuleFor(x => x.Cc).Emails();
|
||||
RuleFor(x => x.Bcc).Emails();
|
||||
|
||||
if (dispatcher != null)
|
||||
{
|
||||
RuleFor(x => x.SenderEmail).MustAsync(async (value, ct) =>
|
||||
{
|
||||
return await dispatcher.IsSenderSupported(value);
|
||||
}).WithMessage("@mailTemplate.errors.senderNotAllowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Raven.Client.Documents;
|
||||
|
||||
namespace zero.Mails;
|
||||
@@ -22,17 +22,9 @@ public class MailTemplatesStore : EntityStore<MailTemplate>, IMailTemplatesStore
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ValidationRules(ZeroValidator<MailTemplate> validator)
|
||||
public override Task<ValidationResult> Validate(ZeroValidationContext ctx, MailTemplate model)
|
||||
{
|
||||
validator.RuleFor(x => x.SenderEmail).Email();
|
||||
|
||||
if (Dispatcher != null)
|
||||
{
|
||||
validator.RuleFor(x => x.SenderEmail).MustAsync(async (value, ct) =>
|
||||
{
|
||||
return await Dispatcher.IsSenderSupported(value);
|
||||
}).WithMessage("@mailTemplate.errors.senderNotAllowed");
|
||||
}
|
||||
return new MailTemplateValidator(ctx, Dispatcher).ValidateAsync(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ public class MediaStore : TreeEntityStore<Media>, IMediaStore
|
||||
validator.RuleFor(x => x.Name).Length(2, 120);
|
||||
validator.RuleFor(x => x.IsActive).Equal(true);
|
||||
|
||||
validator.When(x => x.IsFolder, () =>
|
||||
{
|
||||
validator.RuleFor(x => x.ParentId).Exists(Context.Store);
|
||||
});
|
||||
//validator.When(x => x.IsFolder, () =>
|
||||
//{
|
||||
// validator.RuleFor(x => x.ParentId).Exists(Context.Store);
|
||||
//});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,10 +62,10 @@ public abstract class EntityStore<T> : IEntityStore<T> where T : ZeroIdEntity, I
|
||||
public virtual string GetChangeToken(T model) => Operations.GetChangeToken(model);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<Result<T>> Create(T model) => Operations.Create(model, async m => await Validate(m));
|
||||
public virtual Task<Result<T>> Create(T model) => Operations.Create(model, async (m, ctx) => await Validate(ctx, m));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<Result<T>> Update(T model) => Operations.Update(model, async m => await Validate(m));
|
||||
public virtual Task<Result<T>> Update(T model) => Operations.Update(model, async (m, ctx) => await Validate(ctx, m));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<Result<IOrderedEnumerable<T>>> Sort(string[] sortedIds) => Operations.Sort<T>(sortedIds);
|
||||
@@ -74,13 +74,21 @@ public abstract class EntityStore<T> : IEntityStore<T> where T : ZeroIdEntity, I
|
||||
public virtual Task<Result<T>> Delete(T model) => Operations.Delete(model);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<ValidationResult> Validate(T model)
|
||||
public virtual async Task<ValidationResult> Validate(ZeroValidationContext ctx, T model)
|
||||
{
|
||||
ZeroValidator<T> validator = new();
|
||||
ValidationRules(validator);
|
||||
return await validator.ValidateAsync(model);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ValidationResult> Validate(T model) => Validate(new ZeroValidationContext()
|
||||
{
|
||||
Context = Context,
|
||||
Session = Session,
|
||||
Operation = ValidationOp.Unknown
|
||||
}, model);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create rules for validation
|
||||
@@ -162,7 +170,7 @@ public interface IEntityStore<T> where T : ZeroIdEntity, ISupportsFlavors, ISupp
|
||||
/// <summary>
|
||||
/// Validates an entity in this collection
|
||||
/// </summary>
|
||||
Task<ValidationResult> Validate(T model);
|
||||
Task<ValidationResult> Validate(ZeroValidationContext ctx, T model);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an entity with an optional validator
|
||||
|
||||
@@ -5,13 +5,13 @@ namespace zero.Stores;
|
||||
public partial class StoreOperations : IStoreOperations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public virtual Task<Result<T>> Create<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new() => Save(model, validate);
|
||||
public virtual Task<Result<T>> Create<T>(T model, Func<T, ZeroValidationContext, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new() => Save(model, validate);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<Result<T>> Update<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new() => Save(model, validate);
|
||||
public virtual Task<Result<T>> Update<T>(T model, Func<T, ZeroValidationContext, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new() => Save(model, validate);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected virtual async Task<Result<T>> Save<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new()
|
||||
protected virtual async Task<Result<T>> Save<T>(T model, Func<T, ZeroValidationContext, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new()
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
@@ -52,7 +52,13 @@ public partial class StoreOperations : IStoreOperations
|
||||
// run validator
|
||||
if (validate != null)
|
||||
{
|
||||
ValidationResult validation = await validate(model);
|
||||
ValidationResult validation = await validate(model, new ZeroValidationContext()
|
||||
{
|
||||
Context = Context,
|
||||
Session = Session,
|
||||
Operation = isUpdate ? ValidationOp.Update : ValidationOp.Create
|
||||
});
|
||||
|
||||
if (!validation.IsValid)
|
||||
{
|
||||
return Result<T>.Fail(validation);
|
||||
|
||||
@@ -189,12 +189,12 @@ public interface IStoreOperations
|
||||
/// <summary>
|
||||
/// Creates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<Result<T>> Create<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
Task<Result<T>> Create<T>(T model, Func<T, ZeroValidationContext, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
|
||||
/// <summary>
|
||||
/// Updates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<Result<T>> Update<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
Task<Result<T>> Update<T>(T model, Func<T, ZeroValidationContext, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
|
||||
|
||||
/// <inheritdoc />
|
||||
Task<Result<IOrderedEnumerable<T>>> Sort<T>(string[] sortedIds) where T : ZeroIdEntity, ISupportsSorting, new();
|
||||
|
||||
@@ -91,65 +91,6 @@ public static class ValidatorExtensions
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if this value is unique within a collection
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, TProperty> Unique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IZeroStore store) where T : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
|
||||
{
|
||||
bool any = await store.Session().Advanced.AsyncDocumentQuery<T>()
|
||||
.WhereNotEquals(nameof(ZeroIdEntity.Id), entity.Id)
|
||||
.WhereEquals(context.PropertyName.ToPascalCaseId(), value)
|
||||
.AnyAsync(cancellation);
|
||||
|
||||
return !any;
|
||||
}).WithMessage("@errors.forms.not_unique");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if this value is at least set once to the expected value within a collection
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, TProperty> ExpectAnyUnique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IZeroStore store, TProperty expectedValue) where T : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
|
||||
{
|
||||
return await store.Session().Advanced.AsyncDocumentQuery<T>()
|
||||
.WhereNotEquals(nameof(ZeroIdEntity.Id), entity.Id)
|
||||
.WhereEquals(context.PropertyName.ToPascalCaseId(), expectedValue)
|
||||
.AnyAsync(cancellation);
|
||||
}).WithMessage("@errors.forms.not_unique_alone");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Exists<T>(this IRuleBuilder<T, string> ruleBuilder, IZeroStore store) where T : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.Exists<T, T>(store);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Exists<T, TReference>(this IRuleBuilder<T, string> ruleBuilder, IZeroStore store) where T : ZeroEntity where TReference : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, id, context, cancellation) =>
|
||||
{
|
||||
if (id.IsNullOrWhiteSpace())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
TReference model = await store.Session().LoadAsync<TReference>(id);
|
||||
return model != null;
|
||||
}).WithMessage("@errors.forms.reference_notfound");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates a culture identifier
|
||||
/// </summary>
|
||||
@@ -180,11 +121,11 @@ public static class ValidatorExtensions
|
||||
/// <summary>
|
||||
/// Check if this value is unique within a collection
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, TProperty> Unique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IZeroDocumentSession session) where T : ZeroEntity
|
||||
public static IRuleBuilderOptions<T, TProperty> Unique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, ZeroValidationContext ctx) where T : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
|
||||
{
|
||||
bool any = await session.Advanced.AsyncDocumentQuery<T>()
|
||||
bool any = await ctx.Session.Advanced.AsyncDocumentQuery<T>()
|
||||
.WhereNotEquals(nameof(ZeroIdEntity.Id), entity.Id)
|
||||
.WhereEquals(context.PropertyName.ToPascalCaseId(), value)
|
||||
.AnyAsync(cancellation);
|
||||
@@ -197,11 +138,11 @@ public static class ValidatorExtensions
|
||||
/// <summary>
|
||||
/// Check if this value is at least set once to the expected value within a collection
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, TProperty> ExpectAnyUnique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IZeroDocumentSession session, TProperty expectedValue) where T : ZeroEntity
|
||||
public static IRuleBuilderOptions<T, TProperty> ExpectAnyUnique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, ZeroValidationContext ctx, TProperty expectedValue) where T : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
|
||||
{
|
||||
return await session.Advanced.AsyncDocumentQuery<T>()
|
||||
return await ctx.Session.Advanced.AsyncDocumentQuery<T>()
|
||||
.WhereNotEquals(nameof(ZeroIdEntity.Id), entity.Id)
|
||||
.WhereEquals(context.PropertyName.ToPascalCaseId(), expectedValue)
|
||||
.AnyAsync(cancellation);
|
||||
@@ -212,16 +153,16 @@ public static class ValidatorExtensions
|
||||
/// <summary>
|
||||
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Exists<T>(this IRuleBuilder<T, string> ruleBuilder, IZeroDocumentSession session) where T : ZeroEntity
|
||||
public static IRuleBuilderOptions<T, string> Exists<T>(this IRuleBuilder<T, string> ruleBuilder, ZeroValidationContext ctx) where T : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.Exists<T, T>(session);
|
||||
return ruleBuilder.Exists<T, T>(ctx);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Exists<T, TReference>(this IRuleBuilder<T, string> ruleBuilder, IZeroDocumentSession session) where T : ZeroEntity where TReference : ZeroEntity
|
||||
public static IRuleBuilderOptions<T, string> Exists<T, TReference>(this IRuleBuilder<T, string> ruleBuilder, ZeroValidationContext ctx) where T : ZeroEntity where TReference : ZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, id, context, cancellation) =>
|
||||
{
|
||||
@@ -230,8 +171,7 @@ public static class ValidatorExtensions
|
||||
return true;
|
||||
}
|
||||
|
||||
TReference model = await session.LoadAsync<TReference>(id);
|
||||
return model != null;
|
||||
return await ctx.Session.Query<TReference>().AnyAsync(x => x.Id == id);
|
||||
}).WithMessage("@errors.forms.reference_notfound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace zero.Validation;
|
||||
|
||||
public class ZeroValidationContext
|
||||
{
|
||||
public IZeroContext Context { get; set; }
|
||||
|
||||
public IZeroDocumentSession Session { get; set; }
|
||||
|
||||
public ValidationOp Operation { get; set; }
|
||||
}
|
||||
|
||||
public enum ValidationOp
|
||||
{
|
||||
Unknown = 0,
|
||||
Create = 1,
|
||||
Update = 2
|
||||
}
|
||||
Reference in New Issue
Block a user