From af01e4a6a412d3dd84f404d29e925b88491f181c Mon Sep 17 00:00:00 2001 From: glax Date: Sat, 6 Sep 2025 17:02:08 +0200 Subject: [PATCH] NotificationConnector DTO and RequestRecords --- API/APIEndpointRecords/GotifyRecord.cs | 16 ----- API/APIEndpointRecords/NtfyRecord.cs | 17 ----- API/APIEndpointRecords/PushoverRecord.cs | 13 ---- API/Controllers/DTOs/NotificationConnector.cs | 46 ++++++++++++ .../NotificationConnectorController.cs | 72 ++++++++++--------- .../Requests/CreateGotifyConnectorRecord.cs | 37 ++++++++++ .../CreateNotificationConnectorRecord.cs | 46 ++++++++++++ .../Requests/CreateNtfyConnectorRecord.cs | 51 +++++++++++++ .../Requests/CreatePushoverConnectorRecord.cs | 28 ++++++++ .../NotificationConnector.cs | 28 ++------ 10 files changed, 252 insertions(+), 102 deletions(-) delete mode 100644 API/APIEndpointRecords/GotifyRecord.cs delete mode 100644 API/APIEndpointRecords/NtfyRecord.cs delete mode 100644 API/APIEndpointRecords/PushoverRecord.cs create mode 100644 API/Controllers/DTOs/NotificationConnector.cs create mode 100644 API/Controllers/Requests/CreateGotifyConnectorRecord.cs create mode 100644 API/Controllers/Requests/CreateNotificationConnectorRecord.cs create mode 100644 API/Controllers/Requests/CreateNtfyConnectorRecord.cs create mode 100644 API/Controllers/Requests/CreatePushoverConnectorRecord.cs diff --git a/API/APIEndpointRecords/GotifyRecord.cs b/API/APIEndpointRecords/GotifyRecord.cs deleted file mode 100644 index 28e2314..0000000 --- a/API/APIEndpointRecords/GotifyRecord.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace API.APIEndpointRecords; - -public record GotifyRecord(string Name, string Endpoint, string AppToken, int Priority) -{ - public bool Validate() - { - if (Endpoint == string.Empty) - return false; - if (AppToken == string.Empty) - return false; - if (Priority < 0 || Priority > 10) - return false; - - return true; - } -} \ No newline at end of file diff --git a/API/APIEndpointRecords/NtfyRecord.cs b/API/APIEndpointRecords/NtfyRecord.cs deleted file mode 100644 index 8aa3099..0000000 --- a/API/APIEndpointRecords/NtfyRecord.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace API.APIEndpointRecords; - -public record NtfyRecord(string Name, string Endpoint, string Username, string Password, string Topic, int Priority) -{ - public bool Validate() - { - if (Endpoint == string.Empty) - return false; - if (Username == string.Empty) - return false; - if (Password == string.Empty) - return false; - if (Priority < 1 || Priority > 5) - return false; - return true; - } -} \ No newline at end of file diff --git a/API/APIEndpointRecords/PushoverRecord.cs b/API/APIEndpointRecords/PushoverRecord.cs deleted file mode 100644 index 6086da6..0000000 --- a/API/APIEndpointRecords/PushoverRecord.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace API.APIEndpointRecords; - -public record PushoverRecord(string Name, string AppToken, string User) -{ - public bool Validate() - { - if (AppToken == string.Empty) - return false; - if (User == string.Empty) - return false; - return true; - } -} \ No newline at end of file diff --git a/API/Controllers/DTOs/NotificationConnector.cs b/API/Controllers/DTOs/NotificationConnector.cs new file mode 100644 index 0000000..66ded8c --- /dev/null +++ b/API/Controllers/DTOs/NotificationConnector.cs @@ -0,0 +1,46 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace API.Controllers.DTOs; + +public record NotificationConnector(string Name, string Url, string HttpMethod, string Body, Dictionary Headers) : Identifiable(Name) +{ + /// + /// The Name of the Notification Connector + /// + [Required] + [Description("The Name of the Notification Connector")] + public string Name { get; init; } = Name; + + /// + /// The Url of the Instance + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Url] + [Description("The Url of the Instance")] + public string Url { get; internal set; } = Url; + + /// + /// The HTTP Request Method to use for notifications + /// + [Required] + [Description("The HTTP Request Method to use for notifications")] + public string HttpMethod { get; internal set; } = HttpMethod; + + /// + /// The Request Body to use to send notifications + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Description("The Request Body to use to send notifications")] + public string Body { get; internal set; } = Body; + + /// + /// The Request Headers to use to send notifications + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Description("The Request Headers to use to send notifications")] + public Dictionary Headers { get; internal set; } = Headers; +} \ No newline at end of file diff --git a/API/Controllers/NotificationConnectorController.cs b/API/Controllers/NotificationConnectorController.cs index 4e3cdbd..80aa2b4 100644 --- a/API/Controllers/NotificationConnectorController.cs +++ b/API/Controllers/NotificationConnectorController.cs @@ -1,7 +1,7 @@ using System.Text; -using API.APIEndpointRecords; +using API.Controllers.DTOs; +using API.Controllers.Requests; using API.Schema.NotificationsContext; -using API.Schema.NotificationsContext.NotificationConnectors; using Asp.Versioning; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; @@ -29,8 +29,10 @@ public class NotificationConnectorController(NotificationsContext context) : Con { if(await context.NotificationConnectors.ToListAsync(HttpContext.RequestAborted) is not { } result) return TypedResults.InternalServerError(); - - return TypedResults.Ok(result); + + List notificationConnectors = result.Select(n => new NotificationConnector(n.Name, n.Url, n.HttpMethod, n.Body, n.Headers)).ToList(); + + return TypedResults.Ok(notificationConnectors); } /// @@ -46,8 +48,10 @@ public class NotificationConnectorController(NotificationsContext context) : Con { if (await context.NotificationConnectors.FirstOrDefaultAsync(c => c.Name == Name, HttpContext.RequestAborted) is not { } connector) return TypedResults.NotFound(nameof(Name)); - - return TypedResults.Ok(connector); + + NotificationConnector notificationConnector = new NotificationConnector(connector.Name, connector.Url, connector.HttpMethod, connector.Body, connector.Headers); + + return TypedResults.Ok(notificationConnector); } /// @@ -57,16 +61,20 @@ public class NotificationConnectorController(NotificationsContext context) : Con /// ID of the new /// Error during Database Operation [HttpPut] - [ProducesResponseType(Status200OK, "text/plain")] + [ProducesResponseType(Status201Created, "text/plain")] [ProducesResponseType(Status500InternalServerError, "text/plain")] - public async Task, InternalServerError>> CreateConnector ([FromBody]NotificationConnector notificationConnector) + public async Task, InternalServerError>> CreateConnector ([FromBody]CreateNotificationConnectorRecord requestData) { - context.NotificationConnectors.Add(notificationConnector); - context.Notifications.Add(new ("Added new Notification Connector!", notificationConnector.Name, NotificationUrgency.High)); + // TODO validate data + API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector newConnector = + new(requestData.Name, requestData.Url, requestData.Headers, requestData.HttpMethod, requestData.Body); + + context.NotificationConnectors.Add(newConnector); + context.Notifications.Add(new ("Added new Notification Connector!", newConnector.Name, NotificationUrgency.High)); if(await context.Sync(HttpContext.RequestAborted) is { success: false } result) return TypedResults.InternalServerError(result.exceptionMessage); - return TypedResults.Ok(notificationConnector.Name); + return TypedResults.Created(string.Empty, newConnector.Name); } /// @@ -76,17 +84,16 @@ public class NotificationConnectorController(NotificationsContext context) : Con /// ID of the new /// Error during Database Operation [HttpPut("Gotify")] - [ProducesResponseType(Status200OK, "text/plain")] + [ProducesResponseType(Status201Created, "text/plain")] [ProducesResponseType(Status500InternalServerError, "text/plain")] - public async Task, InternalServerError>> CreateGotifyConnector ([FromBody]GotifyRecord gotifyData) + public async Task, InternalServerError>> CreateGotifyConnector ([FromBody]CreateGotifyConnectorRecord createGotifyConnectorData) { //TODO Validate Data - - NotificationConnector gotifyConnector = new (gotifyData.Name, - gotifyData.Endpoint, - new Dictionary() { { "X-Gotify-Key", gotifyData.AppToken } }, + CreateNotificationConnectorRecord gotifyConnector = new (createGotifyConnectorData.Name, + createGotifyConnectorData.Url, "POST", - $"{{\"message\": \"%text\", \"title\": \"%title\", \"Priority\": {gotifyData.Priority}}}"); + $"{{\"message\": \"%text\", \"title\": \"%title\", \"Priority\": {createGotifyConnectorData.Priority}}}", + new () { { "X-Gotify-Key", createGotifyConnectorData.AppToken } }); return await CreateConnector(gotifyConnector); } @@ -97,23 +104,19 @@ public class NotificationConnectorController(NotificationsContext context) : Con /// ID of the new /// Error during Database Operation [HttpPut("Ntfy")] - [ProducesResponseType(Status200OK, "text/plain")] + [ProducesResponseType(Status201Created, "text/plain")] [ProducesResponseType(Status500InternalServerError, "text/plain")] - public async Task, InternalServerError>> CreateNtfyConnector ([FromBody]NtfyRecord ntfyRecord) + public async Task, InternalServerError>> CreateNtfyConnector ([FromBody]CreateNtfyConnectorRecord createNtfyConnectorRecord) { //TODO Validate Data - - string authHeader = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ntfyRecord.Username}:{ntfyRecord.Password}")); + string authHeader = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{createNtfyConnectorRecord.Username}:{createNtfyConnectorRecord.Password}")); string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(authHeader)).Replace("=",""); - NotificationConnector ntfyConnector = new (ntfyRecord.Name, - $"{ntfyRecord.Endpoint}?auth={auth}", - new Dictionary() - { - {"Authorization", auth} - }, + CreateNotificationConnectorRecord ntfyConnector = new (createNtfyConnectorRecord.Name, + $"{createNtfyConnectorRecord.Url}?auth={auth}", "POST", - $"{{\"message\": \"%text\", \"title\": \"%title\", \"Priority\": {ntfyRecord.Priority} \"Topic\": \"{ntfyRecord.Topic}\"}}"); + $"{{\"message\": \"%text\", \"title\": \"%title\", \"Priority\": {createNtfyConnectorRecord.Priority} \"Topic\": \"{createNtfyConnectorRecord.Topic}\"}}", + new () {{"Authorization", auth}}); return await CreateConnector(ntfyConnector); } @@ -124,17 +127,16 @@ public class NotificationConnectorController(NotificationsContext context) : Con /// ID of the new /// Error during Database Operation [HttpPut("Pushover")] - [ProducesResponseType(Status200OK, "text/plain")] + [ProducesResponseType(Status201Created, "text/plain")] [ProducesResponseType(Status500InternalServerError, "text/plain")] - public async Task, InternalServerError>> CreatePushoverConnector ([FromBody]PushoverRecord pushoverRecord) + public async Task, InternalServerError>> CreatePushoverConnector ([FromBody]CreatePushoverConnectorRecord createPushoverConnectorRecord) { //TODO Validate Data - - NotificationConnector pushoverConnector = new (pushoverRecord.Name, + CreateNotificationConnectorRecord pushoverConnector = new (createPushoverConnectorRecord.Name, $"https://api.pushover.net/1/messages.json", - new Dictionary(), "POST", - $"{{\"token\": \"{pushoverRecord.AppToken}\", \"user\": \"{pushoverRecord.User}\", \"message:\":\"%text\", \"%title\" }}"); + $"{{\"token\": \"{createPushoverConnectorRecord.AppToken}\", \"user\": \"{createPushoverConnectorRecord.Username}\", \"message:\":\"%text\", \"%title\" }}", + new ()); return await CreateConnector(pushoverConnector); } diff --git a/API/Controllers/Requests/CreateGotifyConnectorRecord.cs b/API/Controllers/Requests/CreateGotifyConnectorRecord.cs new file mode 100644 index 0000000..5d23a36 --- /dev/null +++ b/API/Controllers/Requests/CreateGotifyConnectorRecord.cs @@ -0,0 +1,37 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace API.Controllers.Requests; + +public record CreateGotifyConnectorRecord(string Name, string Url, string AppToken, int Priority) +{ + /// + /// The Name of the Notification Connector + /// + [Required] + [Description("The Name of the Notification Connector")] + public string Name { get; init; } = Name; + + /// + /// The Url of the Instance + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Url] + [Description("The Url of the Instance")] + public string Url { get; internal set; } = Url; + + /// + /// The Apptoken used for authentication + /// + [Required] + [Description("The Apptoken used for authentication")] + public string AppToken { get; init; } = AppToken; + + /// + /// The Priority of Notifications + /// + [Required] + [Description("The Priority of Notifications")] + public int Priority { get; init; } = Priority; +} \ No newline at end of file diff --git a/API/Controllers/Requests/CreateNotificationConnectorRecord.cs b/API/Controllers/Requests/CreateNotificationConnectorRecord.cs new file mode 100644 index 0000000..0cde04b --- /dev/null +++ b/API/Controllers/Requests/CreateNotificationConnectorRecord.cs @@ -0,0 +1,46 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace API.Controllers.Requests; + +public record CreateNotificationConnectorRecord(string Name, string Url, string HttpMethod, string Body, Dictionary Headers) +{ + /// + /// The Name of the Notification Connector + /// + [Required] + [Description("The Name of the Notification Connector")] + public string Name { get; init; } = Name; + + /// + /// The Url of the Instance + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Url] + [Description("The Url of the Instance")] + public string Url { get; internal set; } = Url; + + /// + /// The HTTP Request Method to use for notifications + /// + [Required] + [Description("The HTTP Request Method to use for notifications")] + public string HttpMethod { get; internal set; } = HttpMethod; + + /// + /// The Request Body to use to send notifications + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Description("The Request Body to use to send notifications")] + public string Body { get; internal set; } = Body; + + /// + /// The Request Headers to use to send notifications + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Description("The Request Headers to use to send notifications")] + public Dictionary Headers { get; internal set; } = Headers; +} \ No newline at end of file diff --git a/API/Controllers/Requests/CreateNtfyConnectorRecord.cs b/API/Controllers/Requests/CreateNtfyConnectorRecord.cs new file mode 100644 index 0000000..040dc2f --- /dev/null +++ b/API/Controllers/Requests/CreateNtfyConnectorRecord.cs @@ -0,0 +1,51 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace API.Controllers.Requests; + +public record CreateNtfyConnectorRecord(string Name, string Url, string Username, string Password, string Topic, int Priority) +{ + /// + /// The Name of the Notification Connector + /// + [Required] + [Description("The Name of the Notification Connector")] + public string Name { get; init; } = Name; + + /// + /// The Url of the Instance + /// + /// Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent + [Required] + [Url] + [Description("The Url of the Instance")] + public string Url { get; internal set; } = Url; + + /// + /// The Priority of Notifications + /// + [Required] + [Description("The Priority of Notifications")] + public int Priority { get; init; } = Priority; + + /// + /// The Username used for authentication + /// + [Required] + [Description("The Username used for authentication")] + public string Username { get; init; } = Username; + + /// + /// The Password used for authentication + /// + [Required] + [Description("The Password used for authentication")] + public string Password { get; init; } = Password; + + /// + /// The Topic of Notifications + /// + [Required] + [Description("The Topic of Notifications")] + public string Topic { get; init; } = Topic; +} \ No newline at end of file diff --git a/API/Controllers/Requests/CreatePushoverConnectorRecord.cs b/API/Controllers/Requests/CreatePushoverConnectorRecord.cs new file mode 100644 index 0000000..a0341fb --- /dev/null +++ b/API/Controllers/Requests/CreatePushoverConnectorRecord.cs @@ -0,0 +1,28 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace API.Controllers.Requests; + +public record CreatePushoverConnectorRecord(string Name, string AppToken, string Username) +{ + /// + /// The Name of the Notification Connector + /// + [Required] + [Description("The Name of the Notification Connector")] + public string Name { get; init; } = Name; + + /// + /// The Apptoken used for authentication + /// + [Required] + [Description("The Apptoken used for authentication")] + public string AppToken { get; init; } = AppToken; + + /// + /// The Username used for authentication + /// + [Required] + [Description("The Username used for authentication")] + public string Username { get; init; } = Username; +} \ No newline at end of file diff --git a/API/Schema/NotificationsContext/NotificationConnectors/NotificationConnector.cs b/API/Schema/NotificationsContext/NotificationConnectors/NotificationConnector.cs index 72e37ca..204a84d 100644 --- a/API/Schema/NotificationsContext/NotificationConnectors/NotificationConnector.cs +++ b/API/Schema/NotificationsContext/NotificationConnectors/NotificationConnector.cs @@ -10,36 +10,22 @@ namespace API.Schema.NotificationsContext.NotificationConnectors; [PrimaryKey("Name")] public class NotificationConnector(string name, string url, Dictionary headers, string httpMethod, string body) { - [StringLength(64)] - [Required] - public string Name { get; init; } = name; + [StringLength(64)] public string Name { get; init; } = name; - [StringLength(2048)] - [Required] - [Url] - public string Url { get; internal set; } = url; + [StringLength(2048)] [Url] public string Url { get; internal set; } = url; - [Required] - public Dictionary Headers { get; internal set; } = headers; + [Required] public Dictionary Headers { get; internal set; } = headers; - [StringLength(8)] - [Required] - public string HttpMethod { get; internal set; } = httpMethod; + [StringLength(8)] public string HttpMethod { get; internal set; } = httpMethod; - [StringLength(4096)] - [Required] - public string Body { get; internal set; } = body; + [StringLength(4096)] public string Body { get; internal set; } = body; - [JsonIgnore] - [NotMapped] - private readonly HttpClient Client = new() + [NotMapped] private readonly HttpClient Client = new() { DefaultRequestHeaders = { { "User-Agent", Tranga.Settings.UserAgent } } }; - [JsonIgnore] - [NotMapped] - protected ILog Log = LogManager.GetLogger(name); + [JsonIgnore] protected ILog Log = LogManager.GetLogger(name); public void SendNotification(string title, string notificationText) {