using FluentValidation.Results;
using Microsoft.Extensions.DependencyInjection;
using Raven.Client;
using Raven.Client.Documents.Indexes;
using Raven.Client.Documents.Linq;
using System.Linq.Expressions;
using System.Security.Claims;
namespace Finch.Raven;
public partial class RavenOperations : IRavenOperations
{
///
public IAsyncDocumentSession Session => Store.Session();
protected record OperationOptions(bool IncludeInactive);
protected IFinchContext Context { get; private set; }
protected IInterceptors Interceptors { get; private set; }
protected FlavorOptions Flavors { get; private set; }
protected IServiceProvider Services { get; private set; }
protected IFinchStore 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;
Flavors = context.Options.For();
}
///
public async Task GenerateId(T model) where T : FinchIdEntity
{
IAsyncDocumentSession session = Session;
return await session.Advanced.DocumentStore.Conventions.GenerateDocumentIdAsync(session.Advanced.DocumentStore.Database, model);
}
///
public T AutoSetIds(T model)
{
return IdGenerator.Autofill(model);
}
///
public T PrepareForSave(T model) where T : FinchIdEntity
{
// set IDs
AutoSetIds(model);
if (model is FinchEntity finchModel)
{
// get current user
string userId = null;
//string userId = Context.BackofficeUser.FindFirstValue(Constants.Auth.Claims.UserId).Or(Constants.Auth.SystemUser);
// set default properties
if (finchModel.CreatedDate == default)
{
finchModel.CreatedDate = DateTimeOffset.Now;
}
if (finchModel.CreatedById.IsNullOrEmpty())
{
finchModel.CreatedById = userId;
}
// update name alias and last modified
finchModel.Alias = Safenames.Alias(finchModel.Name);
finchModel.LastModifiedById = userId;
finchModel.LastModifiedDate = DateTimeOffset.Now;
finchModel.CreatedById ??= userId;
finchModel.Hash ??= IdGenerator.Create();
}
return model;
}
///
public async Task Validate(T model) where T : FinchIdEntity, new()
{
IFinchMergedValidator validator = Services.GetService>();
if (validator == null)
{
return new();
}
return await validator.ValidateAsync(model);
}
///
public StoreInterceptorBlocker WithoutInterceptors()
{
return InterceptorBlocker ?? (InterceptorBlocker = new(() => InterceptorBlocker = null));
}
///
public virtual T WhenActive(T model) where T : FinchIdEntity, new()
{
return model; // TODO should we really use this? I tried to get data in a custom backend but couldn't because of this
//return model != null && (model is not FinchEntity || (model as FinchEntity).IsActive) && (model is not ISupportsSoftDelete || !(model as ISupportsSoftDelete).IsDeleted) ? model : default;
}
}
public class StoreInterceptorBlocker : IDisposable
{
Action _onRelease;
internal StoreInterceptorBlocker(Action onRelease)
{
_onRelease = onRelease;
}
public void Dispose()
{
_onRelease();
}
}
public interface IRavenOperations
{
///
/// Access to the current session
///
IAsyncDocumentSession Session { get; }
///
/// Get new instance of an entity (with an optional flavor)
///
Task Empty(string flavorAlias = null) where T : FinchIdEntity, ISupportsFlavors, new();
///
/// Get new instance of an entity with a specific flavor
///
/// Optional alias. If left out the default flavor is used (if configured)
Task Empty(string flavorAlias = null)
where T : FinchIdEntity, ISupportsFlavors, new()
where TFlavor : T, new();
///
/// Generate model Id by using configured document store conventions
///
Task GenerateId(T model) where T : FinchIdEntity;
///
/// Generate values for all properties (incl. nested) which contain the [GenerateId] attribute
///
T AutoSetIds(T model);
///
/// Automatically fill base properties of a FinchEntity
///
T PrepareForSave(T model) where T : FinchIdEntity;
///
/// Check if any items exist in this collection (with optional query)
///
Task Any(Func, IQueryable> querySelector = default) where T : FinchIdEntity, new();
///
/// Get an entity by Id
///
Task Load(string id, string changeVector = null) where T : FinchIdEntity, new();
///
/// Get entities by ids
///
Task> Load(IEnumerable ids) where T : FinchIdEntity, new();
///
/// Get entities by ids
///
Task> LoadAsList(IEnumerable ids) where T : FinchIdEntity, new();
///
/// Get entities by query
///
Task> Load(int pageNumber, int pageSize, Func, IQueryable> expression = default) where T : FinchIdEntity, new();
///
/// Get entities by query (by using the specified index)
///
Task> Load(int pageNumber, int pageSize, Func, IQueryable> expression = default) where T : FinchIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new();
///
/// Get entities by query
///
Task> Load(Func, IQueryable> expression) where T : FinchIdEntity, new();
///
/// Get entities by query (by using the specified index)
///
Task> Load(Func, IQueryable> expression) where T : FinchIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new();
///
/// Get entities by query
///
Task> Load(Expression> predicate) where T : FinchIdEntity, new();
///
/// Get entities by query (by using the specified index)
///
Task> Load(Expression> predicate) where T : FinchIdEntity, new() where TIndex : AbstractCommonApiForIndexes, new();
///
/// Get entities by query (by using the specified index) and project into a result
///
Task> Load(int pageNumber, int pageSize, Func, IQueryable> querySelector = default)
where T : FinchIdEntity, new()
where TProjection : FinchIdEntity, new()
where TIndex : AbstractCommonApiForIndexes, new();
///
/// Get all entities from this collection.
/// Warning: Don't use this method for large collections. Stream the results instead.
///
Task> LoadAll() where T : FinchIdEntity, new();
///
/// Stream the collection
///
IAsyncEnumerable Stream(Func, IQueryable> expression) where T : FinchIdEntity, new();
///
/// Get the change vector for a model
///
string GetChangeToken(T model) where T : FinchIdEntity, new();
///
/// Validates an entity
///
Task Validate(T model) where T : FinchIdEntity, new();
///
/// Do not run interceptors for create/update/delete operations while this disposable is active
///
StoreInterceptorBlocker WithoutInterceptors();
///
/// Do only return the model when it is set to active or inactive entities are included with IncludeInactive()
///
T WhenActive(T model) where T : FinchIdEntity, new();
///
/// Creates an entity with an optional validator
///
Task> Create(T model, Func> validate = null, Action onAfterStore = null) where T : FinchIdEntity, new();
///
/// Updates an entity with an optional validator
///
Task> Update(T model, Func> validate = null, Action onAfterStore = null) where T : FinchIdEntity, new();
///
Task>> Sort(string[] sortedIds) where T : FinchIdEntity, ISupportsSorting, new();
///
/// Batch create entities
///
Task>> CreateAll(IEnumerable models) where T : FinchIdEntity, new();
///
/// Deletes an entity
///
Task> Delete(T model) where T : FinchIdEntity, new();
///
/// Deletes an entity
///
Task> Delete(string id) where T : FinchIdEntity, new();
///
/// Deletes the whole collection
///
Task Purge(string querySuffix = null, Parameters parameters = null) where T : FinchIdEntity, new();
///
/// Loads all children for an entity
///
Task> LoadChildren(string parentId, int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : FinchIdEntity, ISupportsTrees, new();
///
/// Get descendants by query (by using the specified index)
///
Task> LoadChildren(string parentId, int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : FinchIdEntity, ISupportsTrees, new() where TIndex : AbstractCommonApiForIndexes, new();
///
/// Get tree hierarchy for an entity
///
Task GetHierarchy(string id) where T : FinchIdEntity, ISupportsTrees, new() where TIndex : FinchTreeHierarchyIndex, new();
///
/// Move an entity to a new parent
///
Task> Move(string id, string newParentId, Func> isParentAllowed = null) where T : FinchIdEntity, ISupportsTrees, new();
///
/// Copies an entity to a new location
///
Task> Copy(string id, string newParentId, Func> isParentAllowed = null) where T : FinchIdEntity, ISupportsTrees, new();
///
/// Copies an entity with descendants to a new location
///
Task> CopyWithDescendants(string id, string newParentId, Func> isParentAllowed = null) where T : FinchIdEntity, ISupportsTrees, new();
///
/// Deletes an entity with all descendents
///
Task> DeleteWithDescendants(T model) where T : FinchIdEntity, ISupportsTrees, new();
}