using Refit; namespace OTSSignsOrchestrator.Server.Clients; // ── Request DTOs ──────────────────────────────────────────────────────────── public record CreateUserRequest( string UserName, string Email, string Password, int UserTypeId, int HomePageId); public record UpdateUserRequest( string? UserName, string? Email, string? Password, int? UserTypeId, int? HomePageId, int? Retired); public record CreateGroupRequest(string Group, string? Description); public record AssignMemberRequest(int[] UserId); public record SetAclRequest(string[] ObjectId, string[] PermissionsId); public record CreateApplicationRequest(string Name); public record UpdateSettingsRequest(Dictionary Settings); public record CreateDisplayRequest(string Display, string? Description); // ── Xibo CMS Refit Interface ──────────────────────────────────────────────── // CRITICAL: GET /api/application is BLOCKED — only POST and DELETE exist. // All group endpoints use /api/group, NOT /api/usergroup. // Feature assignment is POST /api/group/{id}/acl, NOT /features. // Xibo paginates at 10 items by default — always pass start + length params. [Headers("Authorization: Bearer")] public interface IXiboApiClient { // ── About ─────────────────────────────────────────────────────────────── [Get("/about")] Task> GetAboutAsync(); // ── Users ─────────────────────────────────────────────────────────────── [Get("/user")] Task>> GetUsersAsync( [AliasAs("start")] int? start = 0, [AliasAs("length")] int? length = 200); [Post("/user")] Task>> CreateUserAsync( [Body(BodySerializationMethod.UrlEncoded)] CreateUserRequest body); [Put("/user/{userId}")] Task>> UpdateUserAsync( int userId, [Body(BodySerializationMethod.UrlEncoded)] UpdateUserRequest body); [Delete("/user/{userId}")] Task DeleteUserAsync(int userId); // ── Groups (NOT /usergroup) ───────────────────────────────────────────── [Get("/group")] Task>> GetGroupsAsync( [AliasAs("start")] int? start = 0, [AliasAs("length")] int? length = 200); [Post("/group")] Task>> CreateGroupAsync( [Body(BodySerializationMethod.UrlEncoded)] CreateGroupRequest body); [Delete("/group/{groupId}")] Task DeleteGroupAsync(int groupId); [Post("/group/members/assign/{groupId}")] Task> AssignUserToGroupAsync( int groupId, [Body(BodySerializationMethod.UrlEncoded)] AssignMemberRequest body); // ACL — NOT /features [Post("/group/{groupId}/acl")] Task> SetGroupAclAsync( int groupId, [Body(BodySerializationMethod.UrlEncoded)] SetAclRequest body); // ── Displays ──────────────────────────────────────────────────────────── [Get("/display")] Task>> GetDisplaysAsync( [AliasAs("start")] int? start = 0, [AliasAs("length")] int? length = 200, [AliasAs("authorised")] int? authorised = null); // ── Applications (POST + DELETE only — GET is BLOCKED) ────────────────── [Post("/application")] Task>> CreateApplicationAsync( [Body(BodySerializationMethod.UrlEncoded)] CreateApplicationRequest body); [Delete("/application/{key}")] Task DeleteApplicationAsync(string key); // ── Settings ──────────────────────────────────────────────────────────── [Get("/settings")] Task> GetSettingsAsync(); [Put("/settings")] Task> UpdateSettingsAsync( [Body(BodySerializationMethod.UrlEncoded)] UpdateSettingsRequest body); } // ── Pagination helper ─────────────────────────────────────────────────────── public static class XiboApiClientExtensions { /// /// Pages through a Xibo list endpoint until a page returns fewer items than pageSize. /// public static async Task> GetAllPagesAsync( this IXiboApiClient client, Func>> listMethod, int pageSize = 200) { var all = new List(); var start = 0; while (true) { var page = await listMethod(start, pageSize); all.AddRange(page); if (page.Count < pageSize) break; start += pageSize; } return all; } }