using FluentValidation.Results; using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; using System.Security.Claims; namespace zero.Stores; public partial class StoreOperations : IStoreOperations { /// public IZeroDocumentSession Session => Context.Store.Session(); protected record EntityCollectionOptions(bool IncludeInactive); protected IZeroContext Context { get; private set; } protected EntityCollectionOptions Options { get; set; } protected IInterceptors Interceptors { get; private set; } protected FlavorOptions Flavors { get; private set; } public StoreOperations(IZeroContext context, IInterceptors interceptors, IZeroOptions options) { Context = context; Interceptors = interceptors; Options = new(true); Flavors = options.For(); } /// public async Task GenerateId(T model) where T : ZeroIdEntity { IZeroDocumentSession session = Context.Store.Session(); return await session.Advanced.DocumentStore.Conventions.GenerateDocumentIdAsync(session.Advanced.DocumentStore.Database, model); } /// public T AutoSetIds(T model) { // find all Raven Ids List> ravenIds = ObjectTraverser.FindAttribute(model); // set unset Raven Ids foreach (ObjectTraverser.Result item in ravenIds) { string id = item.Property.GetValue(item.Parent, null) as string; if (id.IsNullOrWhiteSpace()) { id = item.Item.Length.HasValue ? IdGenerator.Create(item.Item.Length.Value) : IdGenerator.Create(); item.Property.SetValue(item.Parent, id); } } return model; } /// public T PrepareForSave(T model) where T : ZeroIdEntity { // set IDs AutoSetIds(model); if (model is ZeroEntity) { ZeroEntity zeroModel = model as ZeroEntity; // 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(); } return model; } /// /// Do only return the model when it is set to active or inactive entities are included with IncludeInactive() /// protected virtual T WhenActive(T model) where T : ZeroIdEntity, new() { return model != null && (Options.IncludeInactive || model is not ZeroEntity || (model as ZeroEntity).IsActive) ? model : default; } } public interface IStoreOperations { /// /// Get new instance of an entity (with an optional flavor) /// Task Empty(string flavorAlias = null) where T : ZeroIdEntity, 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 : ZeroIdEntity, ISupportsFlavors, new() where TFlavor : T, new(); /// /// Generate model Id by using configured document store conventions /// Task GenerateId(T model) where T : ZeroIdEntity; /// /// Generate values for all properties (incl. nested) which contain the [GenerateId] attribute /// T AutoSetIds(T model); /// /// Automatically fill base properties of a ZeroEntity /// T PrepareForSave(T model) where T : ZeroIdEntity; /// /// Get an entity by Id /// Task Load(string id, string changeVector = null) where T : ZeroIdEntity, new(); /// /// Get entities by ids /// Task> Load(IEnumerable ids) where T : ZeroIdEntity, new(); /// /// Get entities by query /// Task> Load(int pageNumber, int pageSize, Func, IQueryable> expression = default) where T : ZeroIdEntity, new(); /// /// Get entities by query (by using the specified index) /// Task> Load(int pageNumber, int pageSize, Func, IQueryable> expression = default) where T : ZeroIdEntity, 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 : ZeroIdEntity, new(); /// /// Stream the collection /// IAsyncEnumerable Stream(Func, IQueryable> expression) where T : ZeroIdEntity, new(); /// /// Get the change vector for a model /// string GetChangeVector(T model) where T : ZeroIdEntity, new(); /// /// Creates an entity with an optional validator /// Task> Create(T model, Func> validate = null) where T : ZeroIdEntity, new(); /// /// Updates an entity with an optional validator /// Task> Update(T model, Func> validate = null) where T : ZeroIdEntity, new(); /// /// Deletes an entity /// Task> Delete(T model) where T : ZeroIdEntity, new(); /// /// Loads all children for an entity /// Task> LoadChildren(string parentId, int pageNumber, int pageSize, Func, IQueryable> querySelector = default) where T : ZeroIdEntity, 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 : ZeroIdEntity, ISupportsTrees, new() where TIndex : AbstractCommonApiForIndexes, new(); /// /// Update sorting of entities on a specific level /// Task>> Sort(string[] sortedIds) where T : ZeroIdEntity, ISupportsTrees, new(); /// /// Move an entity to a new parent /// Task> Move(string id, string newParentId, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new(); /// /// Copies an entity to a new location /// Task> Copy(string id, string newParentId, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new(); /// /// Copies an entity with descendants to a new location /// Task> CopyWithDescendants(string id, string newParentId, Func> isParentAllowed = null) where T : ZeroIdEntity, ISupportsTrees, new(); /// /// Deletes an entity with all descendents /// Task> DeleteWithDescendants(T model) where T : ZeroIdEntity, ISupportsTrees, new(); }