move raven impl to package

This commit is contained in:
2022-12-07 15:20:53 +01:00
parent e2e58f96f2
commit 87bbd0e94c
60 changed files with 304 additions and 180 deletions
@@ -0,0 +1,68 @@
using FluentValidation;
using Raven.Client.Documents;
using System.Globalization;
using zero.Extensions;
using zero.Models;
using zero.Raven;
namespace zero.Raven;
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;
}
return await store.Session().Query<TReference>().AnyAsync(x => x.Id == id);
}).WithMessage("@errors.forms.reference_notfound");
}
}
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace zero.Persistence;
namespace zero.Raven;
public class ConfigureFlavorJsonOptions : IConfigureOptions<JsonOptions>
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace zero.Persistence;
namespace zero.Raven;
/// <summary>
/// A flavor config holds information about a flavor implementation
@@ -1,6 +1,6 @@
using System.Collections.Concurrent;
namespace zero.Persistence;
namespace zero.Raven;
public class FlavorOptions
{
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace zero.Persistence;
namespace zero.Raven;
/// <summary>
/// A flavor provider is attached to an entity (which has ISupportsFlavors) and contains all flavors
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
public class FlavorProviderOptions<TEntity> where TEntity : class, ISupportsFlavors, new()
{
@@ -1,7 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace zero.Persistence;
namespace zero.Raven;
internal class JsonFlavorVariantConverter<T> : JsonDiscriminatorConverter<T> where T : class, ISupportsFlavors, new()
{
@@ -1,7 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace zero.Persistence;
namespace zero.Raven;
public class JsonFlavorVariantConverterFactory : JsonConverterFactory
{
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
/// <summary>
/// Automatically generate ID with the specified length and insert it into this property on entity save
@@ -1,6 +1,6 @@
using System.Text.Json;
namespace zero.Persistence;
namespace zero.Raven;
public class IdGenerator
{
@@ -4,7 +4,7 @@ using Raven.Client.Documents.Indexes.Spatial;
using Raven.Client.Documents.Operations.Attachments;
using System.Linq.Expressions;
namespace zero.Persistence;
namespace zero.Raven;
public abstract class ZeroJavascriptIndex : AbstractJavaScriptIndexCreationTask, IZeroIndexDefinition
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
public static class ZeroIndexExtensions
{
@@ -1,7 +1,7 @@
using Raven.Client.Documents;
using Raven.Client.Documents.Indexes;
namespace zero.Persistence;
namespace zero.Raven;
public class ZeroTreeHierarchyIndexResult : ZeroIdEntity, ISupportsDbConventions
{
@@ -1,4 +1,4 @@
namespace zero.Communication;
namespace zero.Raven;
public abstract partial class Interceptor<T> : Interceptor, IInterceptor<T> where T : ZeroIdEntity
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
namespace zero.Communication;
namespace zero.Raven;
public class InterceptorInstruction<T> where T : ZeroIdEntity, new()
{
@@ -17,6 +17,8 @@ public class InterceptorInstruction<T> where T : ZeroIdEntity, new()
public Result<T> Result { get; private set; }
protected IZeroContext Context { get; private set; }
protected IZeroStore Store { get; private set; }
protected Lazy<IEnumerable<IInterceptor>> Interceptors { get; private set; }
@@ -29,9 +31,10 @@ public class InterceptorInstruction<T> where T : ZeroIdEntity, new()
protected Func<IInterceptor, bool> InterceptorFilter { get; private set; } = x => true;
internal InterceptorInstruction(IInterceptors interceptors, IZeroContext context, Lazy<IEnumerable<IInterceptor>> registrations, ILogger<IInterceptor> logger, InterceptorRunType runtype, T model, T previousModel = null)
internal InterceptorInstruction(IInterceptors interceptors, IZeroStore store, IZeroContext context, Lazy<IEnumerable<IInterceptor>> registrations, ILogger<IInterceptor> logger, InterceptorRunType runtype, T model, T previousModel = null)
{
InterceptorHandler = interceptors;
Store = store;
Context = context;
Guid = Guid.NewGuid();
Logger = logger;
@@ -65,7 +68,7 @@ public class InterceptorInstruction<T> where T : ZeroIdEntity, new()
InterceptorParameters parameters = new()
{
Context = Context,
Store = Context.Store,
Store = Store,
Properties = new(),
Interceptors = InterceptorHandler,
Operations = operations,
@@ -1,4 +1,4 @@
namespace zero.Communication;
namespace zero.Raven;
public class InterceptorParameters
{
@@ -1,4 +1,4 @@
namespace zero.Communication;
namespace zero.Raven;
public class InterceptorResult<T>
{
@@ -1,4 +1,4 @@
namespace zero.Communication;
namespace zero.Raven;
public enum InterceptorRunType
{
@@ -1,32 +1,35 @@
using Microsoft.Extensions.Logging;
namespace zero.Communication;
namespace zero.Raven;
public class Interceptors : IInterceptors
{
protected IZeroContext Context { get; set; }
protected IZeroStore Store { get; set; }
protected Lazy<IEnumerable<IInterceptor>> Registrations { get; set; }
protected ILogger<IInterceptor> Logger { get; set; }
public Interceptors(IZeroContext context, Lazy<IEnumerable<IInterceptor>> registrations, ILogger<IInterceptor> logger)
public Interceptors(IZeroContext context, IZeroStore store, Lazy<IEnumerable<IInterceptor>> registrations, ILogger<IInterceptor> logger)
{
Context = context;
Store = store;
Registrations = registrations;
Logger = logger;
}
/// <inheritdoc />
public InterceptorInstruction<T> ForCreate<T>(T model) where T : ZeroIdEntity, new() => new(this, Context, Registrations, Logger, InterceptorRunType.Create, model);
public InterceptorInstruction<T> ForCreate<T>(T model) where T : ZeroIdEntity, new() => new(this, Store, Context, Registrations, Logger, InterceptorRunType.Create, model);
/// <inheritdoc />
public InterceptorInstruction<T> ForUpdate<T>(T model, T previousModel = null) where T : ZeroIdEntity, new() => new(this, Context, Registrations, Logger, InterceptorRunType.Update, model, previousModel);
public InterceptorInstruction<T> ForUpdate<T>(T model, T previousModel = null) where T : ZeroIdEntity, new() => new(this, Store, Context, Registrations, Logger, InterceptorRunType.Update, model, previousModel);
/// <inheritdoc />
public InterceptorInstruction<T> ForDelete<T>(T model) where T : ZeroIdEntity, new() => new(this, Context, Registrations, Logger, InterceptorRunType.Delete, model);
public InterceptorInstruction<T> ForDelete<T>(T model) where T : ZeroIdEntity, new() => new(this, Store, Context, Registrations, Logger, InterceptorRunType.Delete, model);
}
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
public partial class RavenOperations : IRavenOperations
{
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
public partial class RavenOperations : IRavenOperations
{
@@ -3,7 +3,7 @@ using Raven.Client.Documents.Indexes;
using Raven.Client.Documents.Linq;
using Raven.Client.Documents.Session;
namespace zero.Persistence;
namespace zero.Raven;
public partial class RavenOperations : IRavenOperations
{
@@ -3,7 +3,7 @@ using Raven.Client.Documents.Indexes;
using Raven.Client.Documents.Linq;
using Raven.Client.Documents.Session;
namespace zero.Persistence;
namespace zero.Raven;
public partial class RavenOperations : IRavenOperations
{
@@ -1,6 +1,7 @@
using FluentValidation.Results;
using Rv = Raven.Client;
namespace zero.Persistence;
namespace zero.Raven;
public partial class RavenOperations : IRavenOperations
{
@@ -24,7 +25,7 @@ public partial class RavenOperations : IRavenOperations
// check if the Id for a model already exists
if (!model.Id.IsNullOrEmpty())
{
using (IZeroDocumentSession session = Context.Store.Session(options: new Raven.Client.Documents.Session.SessionOptions() { Database = Context.Store.ResolvedDatabase, NoCaching = true }))
using (IZeroDocumentSession session = Store.Session(options: new Rv.Documents.Session.SessionOptions() { Database = Store.ResolvedDatabase, NoCaching = true }))
{
previousModel = await session.LoadAsync<T>(model.Id);
}
@@ -4,12 +4,12 @@ using Raven.Client.Documents.Indexes;
using Raven.Client.Documents.Linq;
using System.Security.Claims;
namespace zero.Persistence;
namespace zero.Raven;
public partial class RavenOperations : IRavenOperations
{
/// <inheritdoc />
public IZeroDocumentSession Session => Context.Store.Session();
public IZeroDocumentSession Session => Store.Session();
protected record OperationOptions(bool IncludeInactive);
@@ -20,12 +20,15 @@ public partial class RavenOperations : IRavenOperations
protected FlavorOptions Flavors { get; private set; }
protected IServiceProvider Services { get; private set; }
protected IZeroStore Store { get; private set; }
protected StoreInterceptorBlocker InterceptorBlocker { get; private set; }
public RavenOperations(StoreContext context)
{
Store = context.Store;
Context = context.Context;
Interceptors = context.Interceptors;
Services = context.Services;
@@ -1,6 +1,6 @@
using Raven.Client.Documents.Linq;
namespace zero.Persistence;
namespace zero.Raven;
public static class RavenOperationsExtensions
{
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
public class StoreContext
{
@@ -15,9 +15,9 @@ public class StoreContext
public IMessageAggregator Messages { get; private set; }
public StoreContext(IZeroContext context, IInterceptors interceptors, IServiceProvider serviceProvider, IMessageAggregator messages)
public StoreContext(IZeroContext context, IZeroStore store, IInterceptors interceptors, IServiceProvider serviceProvider, IMessageAggregator messages)
{
Store = context.Store;
Store = store;
Options = context.Options;
Context = context;
Interceptors = interceptors;
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
/// <summary>
/// This attribute will allow the usage of custom collection names for Raven collections
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
public static partial class RavenConstants
{
@@ -1,6 +1,6 @@
using Raven.Client.Documents.Indexes;
namespace zero.Persistence;
namespace zero.Raven;
public static class RavenIndexExtensions
{
@@ -1,7 +1,7 @@
using Raven.Client.Documents;
using System.Linq.Expressions;
namespace zero.Persistence;
namespace zero.Raven;
public class RavenOptions
{
@@ -4,7 +4,7 @@ using Raven.Client.Documents.Queries;
using Raven.Client.Documents.Session;
using System.Linq.Expressions;
namespace zero.Persistence;
namespace zero.Raven;
public static class RavenQueryableExtensions
{
@@ -1,7 +1,7 @@
using System.IO;
using System.Text;
namespace zero.Persistence;
namespace zero.Raven;
public class Safenames
{
@@ -3,7 +3,7 @@ using System.Net;
using System.Security.Cryptography;
using System.Text;
namespace zero.Persistence;
namespace zero.Raven;
/// <summary>
/// Replicates the Rfc6238AuthenticationService from ASP.NET Core internals as it isn't part of the public API
@@ -1,4 +1,4 @@
namespace zero.Persistence;
namespace zero.Raven;
[RavenCollection("Tokens")]
public class SecurityToken : ISupportsDbConventions
@@ -3,7 +3,7 @@ using Raven.Client.Documents.Session;
using System.Security.Cryptography;
using System.Text;
namespace zero.Persistence;
namespace zero.Raven;
public class ZeroTokenProvider : IZeroTokenProvider
{
+19
View File
@@ -0,0 +1,19 @@
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using zero.Utils;
global using zero.Configuration;
global using zero.Extensions;
global using zero.FileStorage;
global using zero.Models;
global using zero.Architecture;
global using zero.Rendering;
global using zero.Validation;
global using zero.Localization;
global using zero.Context;
global using zero.Communication;
+12
View File
@@ -0,0 +1,12 @@
using zero.Raven;
namespace zero;
public static class ZeroBuilderExtensions
{
public static ZeroBuilder AddRavenDb(this ZeroBuilder builder)
{
builder.AddModule<ZeroRavenModule>();
return builder;
}
}
@@ -2,8 +2,9 @@
using System.Collections.Concurrent;
using System.Reflection;
using System.Text;
using Rv = Raven.Client;
namespace zero.Persistence;
namespace zero.Raven;
public class ZeroDocumentConventionsBuilder : IZeroDocumentConventionsBuilder
{
@@ -67,7 +68,7 @@ public class ZeroDocumentConventionsBuilder : IZeroDocumentConventionsBuilder
};
// get inner type for revisions
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Raven.Client.Documents.Subscriptions.Revision<>))
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Rv.Documents.Subscriptions.Revision<>))
{
type = type.GetGenericArguments().FirstOrDefault();
}
@@ -1,7 +1,7 @@
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
namespace zero.Persistence;
namespace zero.Raven;
public class ZeroDocumentSession : AsyncDocumentSession, IZeroDocumentSession
{
@@ -0,0 +1,16 @@
using Rv = Raven.Client;
namespace zero.Raven;
public static class ZeroDocumentSessionExtensions
{
public static void SetCollection<T>(this IZeroDocumentSession session, T model, string collectionName)
{
session.Advanced.GetMetadataFor(model)[Rv.Constants.Documents.Metadata.Collection] = collectionName;
}
public static void Expires<T>(this IZeroDocumentSession session, T model, TimeSpan expires)
{
session.Advanced.GetMetadataFor(model)[Rv.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddSeconds(expires.TotalSeconds);
}
}
@@ -5,7 +5,7 @@ using Raven.Client.Documents.Operations;
using Raven.Client.Documents.Queries;
using Raven.Client.Documents.Session;
namespace zero.Persistence;
namespace zero.Raven;
public class ZeroDocumentStore : DocumentStore, IZeroDocumentStore
{
@@ -4,9 +4,9 @@ using Raven.Client.Documents;
using Raven.Client.Documents.Indexes;
using Raven.Client.Http;
namespace zero.Persistence;
namespace zero.Raven;
internal class ZeroPersistenceModule : ZeroModule
internal class ZeroRavenModule : ZeroModule
{
public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
@@ -16,6 +16,7 @@ internal class ZeroPersistenceModule : ZeroModule
services.AddScoped<IZeroTokenProvider, ZeroTokenProvider>();
services.AddScoped<StoreContext>();
services.AddTransient<IRavenOperations, RavenOperations>();
services.AddScoped<IInterceptors, Interceptors>();
services.AddOptions<FlavorOptions>();
services.AddOptions<RavenOptions>().Bind(configuration.GetSection("Zero:Raven"));
@@ -50,12 +51,10 @@ internal class ZeroPersistenceModule : ZeroModule
IDocumentStore raven = store.Initialize();
// create all indexes
if (options.Initialized)
{
var indexes = ravenOptions.Indexes.BuildAll(options, store);
IndexCreation.CreateIndexes(indexes, store, database: ravenOptions.Database);
}
IEnumerable<IZeroIndexDefinition> indexes = ravenOptions.Indexes.BuildAll(options, store);
IndexCreation.CreateIndexes(indexes, store, database: ravenOptions.Database);
return (ZeroDocumentStore)raven;
}
}
@@ -1,6 +1,6 @@
using Raven.Client.Documents.Session;
namespace zero.Persistence;
namespace zero.Raven;
public class ZeroStore : IZeroStore
{
+22
View File
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>zero.Raven</PackageId>
<Version>1.0.0</Version>
<LangVersion>preview</LangVersion>
<TargetFramework>net7.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<RootNamespace>zero.Raven</RootNamespace>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RavenDB.Client" Version="5.4.5" />
<PackageReference Include="FluentValidation" Version="11.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\zero\zero.csproj" />
</ItemGroup>
</Project>
+6
View File
@@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zero", "zero\zero.csproj",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "legacy", "legacy", "{93A1CCEB-A323-445E-8116-4575C5939B46}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zero.Raven", "zero.Raven\zero.Raven.csproj", "{DC79D7EA-FAA1-4701-9C36-8295CD1C45D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,6 +45,10 @@ Global
{33CD6E46-CD81-42C3-9019-C0EAD557EE99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33CD6E46-CD81-42C3-9019-C0EAD557EE99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33CD6E46-CD81-42C3-9019-C0EAD557EE99}.Release|Any CPU.Build.0 = Release|Any CPU
{DC79D7EA-FAA1-4701-9C36-8295CD1C45D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC79D7EA-FAA1-4701-9C36-8295CD1C45D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC79D7EA-FAA1-4701-9C36-8295CD1C45D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC79D7EA-FAA1-4701-9C36-8295CD1C45D0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -7,7 +7,6 @@ internal class ZeroCommunicationModule : ZeroModule
{
public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped<IInterceptors, Interceptors>();
services.AddScoped<IMessageAggregator, MessageAggregator>();
services.AddTransient<IHandlerHolder, HandlerHolder>();
services.AddTransient(typeof(Lazy<>), typeof(LazilyResolved<>));
-8
View File
@@ -6,9 +6,6 @@ namespace zero.Configuration;
public class ZeroOptions : IZeroOptions
{
/// <inheritdoc />
public bool Initialized => !string.IsNullOrEmpty(For<RavenOptions>()?.Database);
/// <inheritdoc />
public string Version { get; set; }
@@ -46,11 +43,6 @@ public interface IZeroOptions
/// </summary>
string Version { get; set; }
/// <summary>
/// Whether this zero instance is initialized (setup is completed)
/// </summary>
bool Initialized { get; }
/// <summary>
/// Expiration of a generated change token for an entity
/// </summary>
+1 -11
View File
@@ -9,10 +9,6 @@ public class ZeroContext : IZeroContext
{
/// <inheritdoc />
public IZeroOptions Options { get; protected set; }
/// <inheritdoc />
public IZeroStore Store { get; private set; }
/// <inheritdoc />
public IServiceProvider Services { get; private set; }
@@ -31,12 +27,11 @@ public class ZeroContext : IZeroContext
public ZeroContext(IZeroOptions options, IHttpContextAccessor httpContextAccessor, ICultureResolver cultureResolver,
ILogger<ZeroContext> logger, IZeroStore store, IHandlerHolder handler, IServiceProvider services)
ILogger<ZeroContext> logger, IHandlerHolder handler, IServiceProvider services)
{
Options = options;
CultureResolver = cultureResolver;
Logger = logger;
Store = store;
Handler = handler;
ValueCollection = new PrimitiveTypeCollection();
HttpContextAccessor = httpContextAccessor;
@@ -80,11 +75,6 @@ public interface IZeroContext
/// </summary>
IZeroOptions Options { get; }
/// <summary>
/// Document store
/// </summary>
IZeroStore Store { get; }
/// <summary>
/// Service container
/// </summary>
+48
View File
@@ -0,0 +1,48 @@
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using System.Text;
using Microsoft.Extensions.Options;
namespace zero.Localization;
public class FileLocalizer : Localizer
{
private List<Translation> _fileList;
private IWebHostEnvironment _env;
public FileLocalizer(IWebHostEnvironment env, IOptionsMonitor<LocalizationOptions> options) : base()
{
_env = env;
LoadIntoCache(options.CurrentValue.FilePath);
options.OnChange((opts, _) => LoadIntoCache(opts.FilePath));
}
protected void LoadIntoCache(string filePath)
{
string path = Path.Combine(_env.ContentRootPath, filePath);
if (!File.Exists(path))
{
_fileList = new();
}
else
{
Dictionary<string, string> texts = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(path, Encoding.Latin1));
_fileList = texts.Select(kvp => new Translation() { Key = kvp.Key, Value = kvp.Value }).ToList();
}
}
protected override Translation LoadTranslation(string key)
{
//return new Translation()
//{
// Value = "{@" + key + "}"
//};
return _fileList.FirstOrDefault(x => x.Key == key);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace zero.Localization;
public class LocalizationOptions
{
public string FilePath { get; set; }
}
+2 -12
View File
@@ -3,17 +3,10 @@ using System.Reflection;
namespace zero.Localization;
public class Localizer : ILocalizer
public abstract class Localizer : ILocalizer
{
protected ConcurrentDictionary<string, string> Cache { get; private set; } = new();
protected IRavenOperations Raven { get; private set; }
public Localizer(IRavenOperations raven)
{
Raven = raven;
}
/// <inheritdoc />
public string Text(string key) => Text(key, null);
@@ -76,10 +69,7 @@ public class Localizer : ILocalizer
/// <summary>
/// Get translation from database or any other source
/// </summary>
protected virtual Translation LoadTranslation(string key)
{
return Raven.Session.Synchronous.Query<Translation>().FirstOrDefault(x => x.Key == key);
}
protected abstract Translation LoadTranslation(string key);
}
public interface ILocalizer
+7 -8
View File
@@ -1,13 +1,12 @@
namespace zero.Localization;
[RavenCollection("Translations")]
public class Translation : ZeroEntity, IAlwaysActive
public class Translation
{
public Translation()
{
IsActive = true;
}
/// <summary>
/// Key of the translation
/// </summary>
public string Key { get; set; }
/// <summary>
/// Value of the translation
/// </summary>
@@ -23,5 +22,5 @@ public class Translation : ZeroEntity, IAlwaysActive
public enum TranslationDisplay
{
Text = 0,
HTML = 1
Html = 1
}
+6 -1
View File
@@ -10,6 +10,11 @@ internal class ZeroLocalizationModule : ZeroModule
{
services.AddScoped<ICultureResolver, CultureResolver>();
services.AddScoped<ICultureService, CultureService>();
services.AddScoped<ILocalizer, Localizer>();
services.AddScoped<ILocalizer, FileLocalizer>();
services.AddOptions<LocalizationOptions>().Bind(configuration.GetSection("Zero:Localization")).Configure(opts =>
{
opts.FilePath = "Config/texts.json";
});
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace zero.Models;
/// <summary>
/// Triggers custom conventions for database operations
/// </summary>
public interface ISupportsDbConventions { }
@@ -1,6 +0,0 @@
namespace zero.Persistence;
/// <summary>
/// Triggers custom Raven conventions for database operations
/// </summary>
public interface ISupportsDbConventions { }
@@ -1,14 +0,0 @@
namespace zero.Persistence;
public static class ZeroDocumentSessionExtensions
{
public static void SetCollection<T>(this IZeroDocumentSession session, T model, string collectionName)
{
session.Advanced.GetMetadataFor(model)[Raven.Client.Constants.Documents.Metadata.Collection] = collectionName;
}
public static void Expires<T>(this IZeroDocumentSession session, T model, TimeSpan expires)
{
session.Advanced.GetMetadataFor(model)[Raven.Client.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddSeconds(expires.TotalSeconds);
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using zero.Persistence;
//global using zero.Persistence;
global using zero.Utils;
global using zero.Configuration;
global using zero.Extensions;
-60
View File
@@ -114,64 +114,4 @@ public static class ValidatorExtensions
}
}).WithMessage("@errors.forms.culture");
}
/// <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;
}
return await store.Session().Query<TReference>().AnyAsync(x => x.Id == id);
}).WithMessage("@errors.forms.reference_notfound");
}
}
+17 -1
View File
@@ -47,7 +47,6 @@ public class ZeroBuilder
//Modules.Add<ZeroMapperModule>();
//Modules.Add<ZeroMediaModule>();
//Modules.Add<ZeroPageModule>();
Modules.Add<ZeroPersistenceModule>();
Modules.Add<ZeroRenderingModule>();
//Modules.Add<ZeroRoutingModule>();
@@ -66,4 +65,21 @@ public class ZeroBuilder
Services.Configure(configureOptions);
return this;
}
public ZeroBuilder AddModule(IZeroModule module)
{
module.ConfigureServices(Services, _configuration);
Modules.Add(module);
return this;
}
public ZeroBuilder AddModule<T>() where T : class, IZeroModule, new()
{
T module = new();
module.ConfigureServices(Services, _configuration);
Modules.Add(module);
return this;
}
}