Files
mixtape/zero.Core/Stores/StoreOperations.cs
T

246 lines
8.1 KiB
C#
Raw Normal View History

2021-11-23 11:35:01 +01:00
using FluentValidation.Results;
using Raven.Client.Documents.Indexes;
using Raven.Client.Documents.Linq;
using System.Security.Claims;
2021-11-23 22:56:22 +01:00
namespace zero.Stores;
2021-11-23 11:35:01 +01:00
2021-12-28 11:33:41 +01:00
public partial class StoreOperations :
IStoreOperations,
ISharedStoreOperations,
IStoreOperationsWithInactive,
ISharedStoreOperationsWithInactive
2021-11-23 11:35:01 +01:00
{
/// <inheritdoc />
2021-12-26 01:42:21 +01:00
public IZeroDocumentSession Session => Context.Store.Session(_overrideDatabase ?? Config.Database);
2021-11-23 11:35:01 +01:00
2021-12-28 11:33:41 +01:00
/// <inheritdoc />
public StoreConfig Config { get; set; }
2021-12-11 15:24:47 +01:00
protected record OperationOptions(bool IncludeInactive);
2021-11-23 11:35:01 +01:00
protected IZeroContext Context { get; private set; }
protected IInterceptors Interceptors { get; private set; }
2021-12-02 13:43:04 +01:00
protected FlavorOptions Flavors { get; private set; }
2021-11-23 11:35:01 +01:00
2021-12-13 15:17:47 +01:00
string _overrideDatabase = null;
2021-12-28 11:33:41 +01:00
public StoreOperations(IStoreContext context, StoreConfig config = null)
2021-11-23 11:35:01 +01:00
{
2021-12-28 11:33:41 +01:00
Context = context.Context;
Interceptors = context.Interceptors;
Flavors = context.Options.For<FlavorOptions>();
Config = config ?? new();
2021-11-23 11:35:01 +01:00
}
/// <inheritdoc />
public async Task<string> GenerateId<T>(T model) where T : ZeroIdEntity
{
2021-12-13 15:17:47 +01:00
IZeroDocumentSession session = Session;
2021-11-23 11:35:01 +01:00
return await session.Advanced.DocumentStore.Conventions.GenerateDocumentIdAsync(session.Advanced.DocumentStore.Database, model);
}
/// <inheritdoc />
public T AutoSetIds<T>(T model)
{
2022-01-06 17:03:05 +01:00
return IdGenerator.Autofill(model);
2021-11-23 11:35:01 +01:00
}
/// <inheritdoc />
public T PrepareForSave<T>(T model) where T : ZeroIdEntity
{
// set IDs
AutoSetIds(model);
2021-12-29 01:25:35 +01:00
if (model is ZeroEntity zeroModel)
2021-11-23 11:35:01 +01:00
{
// get current user
string userId = Context.BackofficeUser.FindFirstValue(Constants.Auth.Claims.UserId);
// set default properties
if (zeroModel.CreatedDate == default)
{
zeroModel.CreatedDate = DateTimeOffset.Now;
}
if (zeroModel.CreatedById.IsNullOrEmpty())
{
zeroModel.CreatedById = userId;
}
if (zeroModel.LanguageId.IsNullOrEmpty())
{
zeroModel.LanguageId ??= "languages.1-A"; // TODO correct language id
}
// update name alias and last modified
zeroModel.Alias = Safenames.Alias(zeroModel.Name);
zeroModel.LastModifiedById = userId;
zeroModel.LastModifiedDate = DateTimeOffset.Now;
zeroModel.CreatedById ??= userId;
zeroModel.Hash ??= IdGenerator.Create();
}
2021-12-29 01:25:35 +01:00
if (model is IAlwaysActive activeModel)
{
activeModel.IsActive = true;
}
2021-11-23 11:35:01 +01:00
return model;
}
/// <summary>
/// Do only return the model when it is set to active or inactive entities are included with IncludeInactive()
/// </summary>
protected virtual T WhenActive<T>(T model) where T : ZeroIdEntity, new()
{
2021-12-29 01:25:35 +01:00
return model != null && (Config.IncludeInactive || model is IAlwaysActive || model is not ZeroEntity || (model as ZeroEntity).IsActive) ? model : default;
2021-11-23 11:35:01 +01:00
}
}
2021-11-23 22:56:22 +01:00
public interface IStoreOperations
2021-11-23 11:35:01 +01:00
{
2021-12-28 11:33:41 +01:00
/// <summary>
/// Access to the current session
/// </summary>
IZeroDocumentSession Session { get; }
/// <summary>
/// Configure operations
/// </summary>
StoreConfig Config { get; set; }
2021-11-23 11:35:01 +01:00
/// <summary>
2021-12-02 16:06:34 +01:00
/// Get new instance of an entity (with an optional flavor)
2021-11-23 11:35:01 +01:00
/// </summary>
2021-12-03 14:45:49 +01:00
Task<T> Empty<T>(string flavorAlias = null) where T : ZeroIdEntity, ISupportsFlavors, new();
2021-12-02 13:43:04 +01:00
/// <summary>
/// Get new instance of an entity with a specific flavor
/// </summary>
2021-12-03 14:45:49 +01:00
/// <param name="flavorAlias">Optional alias. If left out the default flavor is used (if configured)</param>
Task<TFlavor> Empty<T, TFlavor>(string flavorAlias = null)
2021-12-02 13:43:04 +01:00
where T : ZeroIdEntity, ISupportsFlavors, new()
2021-12-02 16:06:34 +01:00
where TFlavor : T, new();
2021-12-02 13:43:04 +01:00
2021-11-23 11:35:01 +01:00
/// <summary>
/// Generate model Id by using configured document store conventions
/// </summary>
Task<string> GenerateId<T>(T model) where T : ZeroIdEntity;
/// <summary>
/// Generate values for all properties (incl. nested) which contain the [GenerateId] attribute
/// </summary>
T AutoSetIds<T>(T model);
/// <summary>
/// Automatically fill base properties of a ZeroEntity
/// </summary>
T PrepareForSave<T>(T model) where T : ZeroIdEntity;
2021-12-22 15:41:11 +01:00
/// <summary>
/// Check if any items exist in this collection (with optional query)
/// </summary>
Task<bool> Any<T>(Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where T : ZeroIdEntity, new();
2021-11-23 11:35:01 +01:00
/// <summary>
/// Get an entity by Id
/// </summary>
Task<T> Load<T>(string id, string changeVector = null) where T : ZeroIdEntity, new();
/// <summary>
/// Get entities by ids
/// </summary>
Task<Dictionary<string, T>> Load<T>(IEnumerable<string> ids) where T : ZeroIdEntity, new();
/// <summary>
/// Get entities by query
/// </summary>
2021-11-23 15:43:21 +01:00
Task<Paged<T>> Load<T>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> expression = default) where T : ZeroIdEntity, new();
2021-11-23 11:35:01 +01:00
/// <summary>
/// Get entities by query (by using the specified index)
/// </summary>
2021-11-23 15:43:21 +01:00
Task<Paged<T>> Load<T, TIndex>(int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> expression = default) where T : ZeroIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new();
2021-11-23 11:35:01 +01:00
/// <summary>
/// Get all entities from this collection.
/// Warning: Don't use this method for large collections. Stream the results instead.
/// </summary>
Task<List<T>> LoadAll<T>() where T : ZeroIdEntity, new();
/// <summary>
/// Stream the collection
/// </summary>
2021-11-23 15:43:21 +01:00
IAsyncEnumerable<T> Stream<T>(Func<IRavenQueryable<T>, IQueryable<T>> expression) where T : ZeroIdEntity, new();
2021-11-23 11:35:01 +01:00
/// <summary>
/// Get the change vector for a model
/// </summary>
2021-12-12 15:41:51 +01:00
string GetChangeToken<T>(T model) where T : ZeroIdEntity, new();
2021-11-23 11:35:01 +01:00
/// <summary>
2021-11-23 15:43:21 +01:00
/// Creates an entity with an optional validator
2021-11-23 11:35:01 +01:00
/// </summary>
2021-11-26 15:47:11 +01:00
Task<Result<T>> Create<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
2021-11-23 15:43:21 +01:00
/// <summary>
/// Updates an entity with an optional validator
/// </summary>
2021-11-26 15:47:11 +01:00
Task<Result<T>> Update<T>(T model, Func<T, Task<ValidationResult>> validate = null) where T : ZeroIdEntity, new();
2021-11-23 11:35:01 +01:00
2021-12-29 15:29:33 +01:00
/// <inheritdoc />
Task<Result<IOrderedEnumerable<T>>> Sort<T>(string[] sortedIds) where T : ZeroIdEntity, ISupportsSorting, new();
2021-11-23 11:35:01 +01:00
/// <summary>
/// Deletes an entity
/// </summary>
2021-11-26 15:47:11 +01:00
Task<Result<T>> Delete<T>(T model) where T : ZeroIdEntity, new();
2021-11-23 11:35:01 +01:00
2021-11-24 13:56:08 +01:00
/// <summary>
/// Loads all children for an entity
/// </summary>
2021-12-01 15:54:11 +01:00
Task<Paged<T>> LoadChildren<T>(string parentId, int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where T : ZeroIdEntity, ISupportsTrees, new();
2021-11-24 13:56:08 +01:00
/// <summary>
/// Get descendants by query (by using the specified index)
/// </summary>
2021-12-01 15:54:11 +01:00
Task<Paged<T>> LoadChildren<T, TIndex>(string parentId, int pageNumber, int pageSize, Func<IRavenQueryable<T>, IQueryable<T>> querySelector = default) where T : ZeroIdEntity, ISupportsTrees, new() where TIndex : AbstractCommonApiForIndexes, new();
2021-11-24 13:56:08 +01:00
2021-12-16 13:55:09 +01:00
/// <summary>
/// Get tree hierarchy for an entity
/// </summary>
Task<T[]> GetHierarchy<T, TIndex>(string id) where T : ZeroIdEntity, ISupportsTrees, new() where TIndex : ZeroTreeHierarchyIndex<T>, new();
2021-11-24 13:56:08 +01:00
/// <summary>
/// Move an entity to a new parent
/// </summary>
2021-12-01 15:54:11 +01:00
Task<Result<T>> Move<T>(string id, string newParentId, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new();
2021-11-24 13:56:08 +01:00
/// <summary>
/// Copies an entity to a new location
/// </summary>
2021-12-01 15:54:11 +01:00
Task<Result<T>> Copy<T>(string id, string newParentId, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new();
2021-11-24 13:56:08 +01:00
/// <summary>
/// Copies an entity with descendants to a new location
/// </summary>
2021-12-01 15:54:11 +01:00
Task<Result<T>> CopyWithDescendants<T>(string id, string newParentId, Func<T, string, Task<bool>> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new();
2021-11-24 13:56:08 +01:00
/// <summary>
/// Deletes an entity with all descendents
/// </summary>
2021-12-01 15:54:11 +01:00
Task<Result<string[]>> DeleteWithDescendants<T>(T model) where T : ZeroIdEntity, ISupportsTrees, new();
2021-12-28 11:33:41 +01:00
}
public interface ISharedStoreOperations : IStoreOperations { }
public interface IStoreOperationsWithInactive : IStoreOperations { }
public interface ISharedStoreOperationsWithInactive : IStoreOperations { }