using Pilz.Data; using Pilz.Extensions.Reflection; using Pilz.Net.Extensions; using System.Diagnostics; using System.Net; namespace Pilz.Net.Api.Server; public abstract class BaseChildItemHandler(IApiServer server) : BaseHandler(server) where TEntity : class, IDataObject where TParent : class, IDataObject where TUpdateMsg : ApiMessage { public class ChildEntityUpdate(TEntity entity, Func getParent, TUpdateMsg message, ApiRequestInfo reqest) : EntityUpdate(entity, message, reqest) { private readonly Func getParent = getParent; private TParent? parent; public TParent? Parent => parent ??= getParent(); } /// /// Gets the base route (endpoint) for the most API calls. /// By default this defines the combo of and . /// public override string Route => RouteParent + RouteChild; /// /// Gets the base route of the parent entity. /// public abstract string RouteParent { get; } /// /// Gets the realtive base route of the child entity. /// public abstract string RouteChild { get; } protected virtual bool RegisterGetAll => true; protected virtual bool RegisterPost => true; protected override void OnInitialize() { base.OnInitialize(); var t = GetType(); if (RegisterGetAll) server.RegisterHandler(t.GetMethod(nameof(GetAll))!.CreateDelegate(this), new(RouteParent + "/{pid}" + RouteChild, "GET"), Debugger.IsAttached); if (RegisterPost) server.RegisterHandler(t.GetMethod(nameof(Post))!.CreateDelegate(this), new(RouteParent + "/{pid}" + RouteChild, "POST"), Debugger.IsAttached); } public virtual ApiResult GetAll(int pid, string? ids, int offset, int amount) { IQueryable entities; if (pid != 0) { if (!server.Manager.Find(pid, out TParent? parentEntity)) return ApiResult.NotFound(); entities = GetChilds(parentEntity).AsQueryable(); } else entities = server.Manager.Get(); entities = SortEntities(FilterByIDs(entities, ids)); if (offset > 0) entities = entities.Skip(offset); if (amount > 0) entities = entities.Take(amount); return SortEntities(entities).ToList().Select(ToClient).ToItemsResult(); } public virtual ApiResult Post(int pid, TUpdateMsg msg, ApiRequestInfo req) { if (!server.Manager.Find(pid, out TParent? parentEntity)) return ApiResult.NotFound(); var entity = CreateNewEntity(msg, parentEntity); var update = new ChildEntityUpdate(entity, () => parentEntity, msg, req); if (UpdateEntity(update) is ApiResult result) return result; GetChilds(parentEntity).Add(entity); if (OnSave(update) is ApiResult result2) return result2; return ToClient(entity).ToItemResult(HttpStatusCode.Created); } public override ApiResult Put(int id, TUpdateMsg msg, ApiRequestInfo req) { if (!server.Manager.Find(id, out TEntity? entity)) return ApiResult.NotFound(); var update = new ChildEntityUpdate(entity, () => server.Manager.Get().AsEnumerable().FirstOrDefault(n => GetChilds(n).Any(n => n.Id == id)), msg, req); if (UpdateEntity(update) is ApiResult result) return result; if (OnSave(update) is ApiResult result2) return result2; return ToClient(entity).ToItemResult(); } protected virtual TEntity CreateNewEntity(TUpdateMsg msg, TParent parent) { return CreateNewEntity(msg); } protected override ApiResult? OnSave(EntityUpdate update) { if (update.Entity.Id == 0 && update is ChildEntityUpdate { Parent: not null } updateEx) { server.Manager.Save(updateEx.Parent, true); return null; } else return base.OnSave(update); } public abstract IList GetChilds(TParent parent); protected virtual IQueryable SortEntities(IQueryable entities) { return entities; } }