NotificationConnector DTO and RequestRecords

This commit is contained in:
2025-09-06 17:02:08 +02:00
parent d4ea40a875
commit af01e4a6a4
10 changed files with 252 additions and 102 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<string, string> Headers) : Identifiable(Name)
{
/// <summary>
/// The Name of the Notification Connector
/// </summary>
[Required]
[Description("The Name of the Notification Connector")]
public string Name { get; init; } = Name;
/// <summary>
/// The Url of the Instance
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Url]
[Description("The Url of the Instance")]
public string Url { get; internal set; } = Url;
/// <summary>
/// The HTTP Request Method to use for notifications
/// </summary>
[Required]
[Description("The HTTP Request Method to use for notifications")]
public string HttpMethod { get; internal set; } = HttpMethod;
/// <summary>
/// The Request Body to use to send notifications
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Description("The Request Body to use to send notifications")]
public string Body { get; internal set; } = Body;
/// <summary>
/// The Request Headers to use to send notifications
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Description("The Request Headers to use to send notifications")]
public Dictionary<string, string> Headers { get; internal set; } = Headers;
}

View File

@@ -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;
@@ -30,7 +30,9 @@ public class NotificationConnectorController(NotificationsContext context) : Con
if(await context.NotificationConnectors.ToListAsync(HttpContext.RequestAborted) is not { } result)
return TypedResults.InternalServerError();
return TypedResults.Ok(result);
List<NotificationConnector> notificationConnectors = result.Select(n => new NotificationConnector(n.Name, n.Url, n.HttpMethod, n.Body, n.Headers)).ToList();
return TypedResults.Ok(notificationConnectors);
}
/// <summary>
@@ -47,7 +49,9 @@ 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);
}
/// <summary>
@@ -57,16 +61,20 @@ public class NotificationConnectorController(NotificationsContext context) : Con
/// <response code="200">ID of the new <see cref="NotificationConnector"/></response>
/// <response code="500">Error during Database Operation</response>
[HttpPut]
[ProducesResponseType<string>(Status200OK, "text/plain")]
[ProducesResponseType<string>(Status201Created, "text/plain")]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public async Task<Results<Ok<string>, InternalServerError<string>>> CreateConnector ([FromBody]NotificationConnector notificationConnector)
public async Task<Results<Created<string>, InternalServerError<string>>> 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);
}
/// <summary>
@@ -76,17 +84,16 @@ public class NotificationConnectorController(NotificationsContext context) : Con
/// <response code="200">ID of the new <see cref="NotificationConnector"/></response>
/// <response code="500">Error during Database Operation</response>
[HttpPut("Gotify")]
[ProducesResponseType<string>(Status200OK, "text/plain")]
[ProducesResponseType<string>(Status201Created, "text/plain")]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public async Task<Results<Ok<string>, InternalServerError<string>>> CreateGotifyConnector ([FromBody]GotifyRecord gotifyData)
public async Task<Results<Created<string>, InternalServerError<string>>> CreateGotifyConnector ([FromBody]CreateGotifyConnectorRecord createGotifyConnectorData)
{
//TODO Validate Data
NotificationConnector gotifyConnector = new (gotifyData.Name,
gotifyData.Endpoint,
new Dictionary<string, string>() { { "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
/// <response code="200">ID of the new <see cref="NotificationConnector"/></response>
/// <response code="500">Error during Database Operation</response>
[HttpPut("Ntfy")]
[ProducesResponseType<string>(Status200OK, "text/plain")]
[ProducesResponseType<string>(Status201Created, "text/plain")]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public async Task<Results<Ok<string>, InternalServerError<string>>> CreateNtfyConnector ([FromBody]NtfyRecord ntfyRecord)
public async Task<Results<Created<string>, InternalServerError<string>>> 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<string, string>()
{
{"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
/// <response code="200">ID of the new <see cref="NotificationConnector"/></response>
/// <response code="500">Error during Database Operation</response>
[HttpPut("Pushover")]
[ProducesResponseType<string>(Status200OK, "text/plain")]
[ProducesResponseType<string>(Status201Created, "text/plain")]
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public async Task<Results<Ok<string>, InternalServerError<string>>> CreatePushoverConnector ([FromBody]PushoverRecord pushoverRecord)
public async Task<Results<Created<string>, InternalServerError<string>>> 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<string, string>(),
"POST",
$"{{\"token\": \"{pushoverRecord.AppToken}\", \"user\": \"{pushoverRecord.User}\", \"message:\":\"%text\", \"%title\" }}");
$"{{\"token\": \"{createPushoverConnectorRecord.AppToken}\", \"user\": \"{createPushoverConnectorRecord.Username}\", \"message:\":\"%text\", \"%title\" }}",
new ());
return await CreateConnector(pushoverConnector);
}

View File

@@ -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)
{
/// <summary>
/// The Name of the Notification Connector
/// </summary>
[Required]
[Description("The Name of the Notification Connector")]
public string Name { get; init; } = Name;
/// <summary>
/// The Url of the Instance
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Url]
[Description("The Url of the Instance")]
public string Url { get; internal set; } = Url;
/// <summary>
/// The Apptoken used for authentication
/// </summary>
[Required]
[Description("The Apptoken used for authentication")]
public string AppToken { get; init; } = AppToken;
/// <summary>
/// The Priority of Notifications
/// </summary>
[Required]
[Description("The Priority of Notifications")]
public int Priority { get; init; } = Priority;
}

View File

@@ -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<string, string> Headers)
{
/// <summary>
/// The Name of the Notification Connector
/// </summary>
[Required]
[Description("The Name of the Notification Connector")]
public string Name { get; init; } = Name;
/// <summary>
/// The Url of the Instance
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Url]
[Description("The Url of the Instance")]
public string Url { get; internal set; } = Url;
/// <summary>
/// The HTTP Request Method to use for notifications
/// </summary>
[Required]
[Description("The HTTP Request Method to use for notifications")]
public string HttpMethod { get; internal set; } = HttpMethod;
/// <summary>
/// The Request Body to use to send notifications
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Description("The Request Body to use to send notifications")]
public string Body { get; internal set; } = Body;
/// <summary>
/// The Request Headers to use to send notifications
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Description("The Request Headers to use to send notifications")]
public Dictionary<string, string> Headers { get; internal set; } = Headers;
}

View File

@@ -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)
{
/// <summary>
/// The Name of the Notification Connector
/// </summary>
[Required]
[Description("The Name of the Notification Connector")]
public string Name { get; init; } = Name;
/// <summary>
/// The Url of the Instance
/// </summary>
/// <remarks>Formatting placeholders: "%title" and "%text" will be replaced when notifications are sent</remarks>
[Required]
[Url]
[Description("The Url of the Instance")]
public string Url { get; internal set; } = Url;
/// <summary>
/// The Priority of Notifications
/// </summary>
[Required]
[Description("The Priority of Notifications")]
public int Priority { get; init; } = Priority;
/// <summary>
/// The Username used for authentication
/// </summary>
[Required]
[Description("The Username used for authentication")]
public string Username { get; init; } = Username;
/// <summary>
/// The Password used for authentication
/// </summary>
[Required]
[Description("The Password used for authentication")]
public string Password { get; init; } = Password;
/// <summary>
/// The Topic of Notifications
/// </summary>
[Required]
[Description("The Topic of Notifications")]
public string Topic { get; init; } = Topic;
}

View File

@@ -0,0 +1,28 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace API.Controllers.Requests;
public record CreatePushoverConnectorRecord(string Name, string AppToken, string Username)
{
/// <summary>
/// The Name of the Notification Connector
/// </summary>
[Required]
[Description("The Name of the Notification Connector")]
public string Name { get; init; } = Name;
/// <summary>
/// The Apptoken used for authentication
/// </summary>
[Required]
[Description("The Apptoken used for authentication")]
public string AppToken { get; init; } = AppToken;
/// <summary>
/// The Username used for authentication
/// </summary>
[Required]
[Description("The Username used for authentication")]
public string Username { get; init; } = Username;
}

View File

@@ -10,36 +10,22 @@ namespace API.Schema.NotificationsContext.NotificationConnectors;
[PrimaryKey("Name")]
public class NotificationConnector(string name, string url, Dictionary<string, string> 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<string, string> Headers { get; internal set; } = headers;
[Required] public Dictionary<string, string> 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)
{