Files
Pilz/Pilz.Net/Api/Server/BaseChildItemHandler.cs
2025-10-09 07:26:42 +02:00

120 lines
4.3 KiB
C#

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<TEntity, TParent, TUpdateMsg>(IApiServer server)
: BaseHandler<TEntity, TUpdateMsg>(server)
where TEntity : class, IDataObject
where TParent : class, IDataObject
where TUpdateMsg : ApiMessage
{
public class ChildEntityUpdate(TEntity entity, Func<TParent?> getParent, TUpdateMsg message, ApiRequestInfo reqest) : EntityUpdate(entity, message, reqest)
{
private readonly Func<TParent?> getParent = getParent;
private TParent? parent;
public TParent? Parent => parent ??= getParent();
}
/// <summary>
/// Gets the base route (endpoint) for the most API calls.
/// By default this defines the combo of <see cref="RouteParent"/> and <see cref="RouteChild"/>.
/// </summary>
public override string Route => RouteParent + RouteChild;
/// <summary>
/// Gets the base route of the parent entity.
/// </summary>
public abstract string RouteParent { get; }
/// <summary>
/// Gets the realtive base route of the child entity.
/// </summary>
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<TEntity> entities;
if (pid != 0)
{
if (!server.Manager.Find(pid, out TParent? parentEntity))
return ApiResult.NotFound();
entities = GetChilds(parentEntity).AsQueryable();
}
else
entities = server.Manager.Get<TEntity>();
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<TParent>().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<TEntity> GetChilds(TParent parent);
protected virtual IQueryable<TEntity> SortEntities(IQueryable<TEntity> entities)
{
return entities;
}
}