From f3f176850855a6d916e541a02b674327b33e7d9d Mon Sep 17 00:00:00 2001 From: Zeegaan Date: Wed, 18 Dec 2024 10:15:43 +0100 Subject: [PATCH 1/3] Add webhook logs endpoint --- .../Webhook/Logs/AllWebhookLogController.cs | 33 ++++++++++++++ .../Webhook/Logs/WebhookLogControllerBase.cs | 9 ++++ .../Factories/IWebhookPresentationFactory.cs | 4 +- .../Factories/WebhookPresentationFactory.cs | 43 ++++++++++++++++++- .../Webhook/Logs/WebhookLogResponseModel.cs | 30 +++++++++++++ 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/WebhookLogControllerBase.cs create mode 100644 src/Umbraco.Cms.Api.Management/ViewModels/Webhook/Logs/WebhookLogResponseModel.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs new file mode 100644 index 000000000000..6ab73019dcd1 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs @@ -0,0 +1,33 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.Webhook; +using Umbraco.Cms.Api.Management.ViewModels.Webhook.Logs; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.Webhook.Logs; + +public class AllWebhookLogController : ManagementApiControllerBase +{ + private readonly IWebhookLogService _webhookLogService; + private readonly IWebhookPresentationFactory _webhookPresentationFactory; + + public AllWebhookLogController(IWebhookLogService webhookLogService, IWebhookPresentationFactory webhookPresentationFactory) + { + _webhookLogService = webhookLogService; + _webhookPresentationFactory = webhookPresentationFactory; + } + + [HttpGet] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(WebhookResponseModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task All(CancellationToken cancellationToken, int skip = 0, int take = 100) + { + PagedModel logs = await _webhookLogService.Get(skip, take); + IEnumerable logResponseModels = logs.Items.Select(x => _webhookPresentationFactory.CreateResponseModel(x)); + return Ok(logResponseModels); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/WebhookLogControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/WebhookLogControllerBase.cs new file mode 100644 index 000000000000..84888184abdb --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/WebhookLogControllerBase.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Routing; +using Umbraco.Cms.Core; + +namespace Umbraco.Cms.Api.Management.Controllers.Webhook.Logs; + +[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Webhook}")] +[ApiExplorerSettings(GroupName = "Webhook")] +public class WebhookLogControllerBase : ManagementApiControllerBase; diff --git a/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs index 421115c832cc..7c63abd6f748 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IWebhookPresentationFactory.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Api.Management.ViewModels.Webhook; +using Umbraco.Cms.Api.Management.ViewModels.Webhook.Logs; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Web.Common.Models; namespace Umbraco.Cms.Api.Management.Factories; @@ -11,4 +11,6 @@ public interface IWebhookPresentationFactory IWebhook CreateWebhook(CreateWebhookRequestModel webhookRequestModel); IWebhook CreateWebhook(UpdateWebhookRequestModel webhookRequestModel, Guid existingWebhookKey); + + WebhookLogResponseModel CreateResponseModel(WebhookLog webhookLog) => new(); } diff --git a/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs index 49a52f53e7c9..018664a37961 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs @@ -1,6 +1,9 @@ using Umbraco.Cms.Api.Management.ViewModels.Webhook; +using Umbraco.Cms.Api.Management.ViewModels.Webhook.Logs; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Webhooks; namespace Umbraco.Cms.Api.Management.Factories; @@ -8,8 +11,18 @@ namespace Umbraco.Cms.Api.Management.Factories; internal class WebhookPresentationFactory : IWebhookPresentationFactory { private readonly WebhookEventCollection _webhookEventCollection; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly ILocalizedTextService _localizedTextService; - public WebhookPresentationFactory(WebhookEventCollection webhookEventCollection) => _webhookEventCollection = webhookEventCollection; + public WebhookPresentationFactory( + WebhookEventCollection webhookEventCollection, + IHostingEnvironment hostingEnvironment, + ILocalizedTextService localizedTextService) + { + _webhookEventCollection = webhookEventCollection; + _hostingEnvironment = hostingEnvironment; + _localizedTextService = localizedTextService; + } public WebhookResponseModel CreateResponseModel(IWebhook webhook) { @@ -44,6 +57,34 @@ public IWebhook CreateWebhook(UpdateWebhookRequestModel webhookRequestModel, Gui return target; } + WebhookLogResponseModel CreateResponseModel(WebhookLog webhookLog) + { + var webhookLogResponseModel = new WebhookLogResponseModel + { + Date = webhookLog.Date, EventAlias = webhookLog.EventAlias, Key = webhookLog.Key, RequestBody = webhookLog.RequestBody ?? string.Empty, + RetryCount = webhookLog.RetryCount, + Url = webhookLog.Url, + RequestHeaders = webhookLog.RequestHeaders, + WebhookKey = webhookLog.WebhookKey, + IsSuccessStatusCode = webhookLog.IsSuccessStatusCode + }; + + if (_hostingEnvironment.IsDebugMode) + { + webhookLogResponseModel.ExceptionOccured = webhookLog.ExceptionOccured; + webhookLogResponseModel.ResponseBody = webhookLog.ResponseBody; + webhookLogResponseModel.ResponseHeaders = webhookLog.ResponseHeaders; + webhookLogResponseModel.StatusCode = webhookLog.StatusCode; + } + else + { + webhookLogResponseModel.ResponseBody = _localizedTextService.Localize("webhooks", "toggleDebug", Thread.CurrentThread.CurrentUICulture); + webhookLogResponseModel.StatusCode = webhookLog.StatusCode is "OK (200)" ? webhookLog.StatusCode : _localizedTextService.Localize("webhooks", "statusNotOk", Thread.CurrentThread.CurrentUICulture); + } + + return webhookLogResponseModel; + } + private WebhookEventResponseModel Create(string alias) { IWebhookEvent? webhookEvent = _webhookEventCollection.FirstOrDefault(x => x.Alias == alias); diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Webhook/Logs/WebhookLogResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Webhook/Logs/WebhookLogResponseModel.cs new file mode 100644 index 000000000000..fab5ee4c1239 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Webhook/Logs/WebhookLogResponseModel.cs @@ -0,0 +1,30 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.Webhook.Logs; + +public class WebhookLogResponseModel +{ + public Guid Key { get; set; } + + public Guid WebhookKey { get; set; } + + public string StatusCode { get; set; } = string.Empty; + + public bool IsSuccessStatusCode { get; set; } + + public DateTime Date { get; set; } + + public string EventAlias { get; set; } = string.Empty; + + public string Url { get; set; } = string.Empty; + + public int RetryCount { get; set; } + + public string RequestHeaders { get; set; } = string.Empty; + + public string RequestBody { get; set; } = string.Empty; + + public string ResponseHeaders { get; set; } = string.Empty; + + public string ResponseBody { get; set; } = string.Empty; + + public bool ExceptionOccured { get; set; } +} From ddeca6f8d38a5c141f5c2e8ac717281fe2f0c75e Mon Sep 17 00:00:00 2001 From: Zeegaan Date: Wed, 18 Dec 2024 10:20:24 +0100 Subject: [PATCH 2/3] Add attribute routing --- .../Controllers/Webhook/Logs/AllWebhookLogController.cs | 3 ++- .../Factories/WebhookPresentationFactory.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs index 6ab73019dcd1..454f1ba35ddd 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs @@ -9,7 +9,8 @@ namespace Umbraco.Cms.Api.Management.Controllers.Webhook.Logs; -public class AllWebhookLogController : ManagementApiControllerBase +[ApiVersion("1.0")] +public class AllWebhookLogController : WebhookLogControllerBase { private readonly IWebhookLogService _webhookLogService; private readonly IWebhookPresentationFactory _webhookPresentationFactory; diff --git a/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs index 018664a37961..491b235664fb 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/WebhookPresentationFactory.cs @@ -57,7 +57,7 @@ public IWebhook CreateWebhook(UpdateWebhookRequestModel webhookRequestModel, Gui return target; } - WebhookLogResponseModel CreateResponseModel(WebhookLog webhookLog) + public WebhookLogResponseModel CreateResponseModel(WebhookLog webhookLog) { var webhookLogResponseModel = new WebhookLogResponseModel { From d93bf2d6953bc0653ea5a240096042d7b279bc61 Mon Sep 17 00:00:00 2001 From: Zeegaan Date: Wed, 18 Dec 2024 10:27:42 +0100 Subject: [PATCH 3/3] Add to open api json --- .../Webhook/Logs/AllWebhookLogController.cs | 4 +- src/Umbraco.Cms.Api.Management/OpenApi.json | 72 ++++++++++++++++++- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs index 454f1ba35ddd..a40b9d4e6a9c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Webhook/Logs/AllWebhookLogController.cs @@ -21,11 +21,11 @@ public AllWebhookLogController(IWebhookLogService webhookLogService, IWebhookPre _webhookPresentationFactory = webhookPresentationFactory; } - [HttpGet] + [HttpGet("logs")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(WebhookResponseModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - public async Task All(CancellationToken cancellationToken, int skip = 0, int take = 100) + public async Task Logs(CancellationToken cancellationToken, int skip = 0, int take = 100) { PagedModel logs = await _webhookLogService.Get(skip, take); IEnumerable logResponseModels = logs.Items.Select(x => _webhookPresentationFactory.CreateResponseModel(x)); diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index a6f3db0530de..7042802f04ca 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -33875,6 +33875,72 @@ } ] } + }, + "/umbraco/management/api/v1/webhook/logs": { + "get": { + "tags": [ + "Webhook" + ], + "operationId": "GetWebhookLogs", + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/WebhookResponseModel" + } + ] + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } } }, "components": { @@ -38339,8 +38405,8 @@ "enum": [ "Healthy", "Unhealthy", - "Corrupt", - "Rebuilding" + "Rebuilding", + "Corrupt" ], "type": "string" }, @@ -46100,4 +46166,4 @@ } } } -} \ No newline at end of file +}