using Microsoft.AspNetCore.Mvc; using Raven.Client.Documents.Indexes; namespace zero.Api.Controllers; public abstract class ZeroApiEntityStoreController : ZeroApiController where TModel : ZeroEntity, new() where TStore : IEntityStore { protected TStore Store { get; set; } public ZeroApiEntityStoreController(TStore store) { Store = store; } protected string GetActionMethod { get; set; } = "Get"; protected virtual string GetAction(TModel model) { return Url.Action(GetActionMethod, new { id = model.Id }); } protected async Task> EmptyModel(string flavorAlias = null, Action modify = null) where T : DisplayModel { TModel model = await Store.Empty(flavorAlias); if (model == null) { return NotFound(); } T result = Mapper.Map(model); modify?.Invoke(result); return result; } protected async Task> GetModel(string id, string changeVector = null) where T : DisplayModel { TModel model = await Store.Load(id, changeVector); if (model == null) { return NotFound(); } HttpContext.Items[ApiConstants.ChangeVector] = Store.GetChangeVector(model); return Mapper.Map(model); } protected async Task> GetModels(ListQuery query) where T : BasicModel { query.OrderQuery ??= q => q.OrderByDescending(x => x.CreatedDate); query.SearchSelector ??= x => x.Name; Paged result = await Store.Load(query.Page, query.PageSize, q => q.Filter(query)); return Mapper.Map(result, (src, dest) => { dest.Link = GetAction(src); }); } protected async Task> GetModels(ListQuery query) where T : BasicModel where TIndex : AbstractCommonApiForIndexes, new() { query.OrderQuery ??= q => q.OrderByDescending(x => x.CreatedDate); query.SearchSelector ??= x => x.Name; Paged result = await Store.Load(query.Page, query.PageSize, q => q.Filter(query)); return Mapper.Map(result, (src, dest) => { dest.Link = GetAction(src); }); } protected async Task> CreateModel(T saveModel) where T : SaveModel where TEdit : DisplayModel { TModel emptyModel = saveModel.Flavor.IsNullOrEmpty() ? await Store.Empty() : await Store.Empty(saveModel.Flavor); TModel model = Mapper.Map(saveModel, emptyModel); Result result = await Store.Create(model); bool minimalResponse = Hints.ResponsePreference == ApiResponsePreference.Minimal; if (result.IsSuccess) { Result mappedResult = Mapper.Map(result); return Created(GetAction(model), minimalResponse ? null : mappedResult); } if (minimalResponse) { return result.WithoutModel(); } return Mapper.Map(result); } protected async Task> UpdateModel(string id, T updateModel, string changeToken = null) where T : SaveModel where TEdit : DisplayModel { if (id != updateModel.Id) { return BadRequest(ApiConstants.HttpErrors.NoIdMatchOnUpdate); } TModel model = await Store.Load(id); if (model == null) { return BadRequest(ApiConstants.HttpErrors.IdNotFound); } if (!changeToken.IsNullOrEmpty() && Store.GetChangeVector(model) != changeToken) { return BadRequest(ApiConstants.HttpErrors.ChangeTokenMismatch); } Mapper.Map(updateModel, model); Result result = await Store.Update(model); if (Hints.ResponsePreference == ApiResponsePreference.Minimal) { return result.WithoutModel(); } // TODO add Preference-Applied header, see https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#76-standard-response-headers return Mapper.Map(result); } protected async Task> DeleteModel(string id) { TModel model = await Store.Load(id); if (model == null) { return NotFound(); } Result result = await Store.Delete(model); return result.WithoutModel(); } }