Webhook log improvements (#16200)
* fix: include all headers in webhook log * feat: return webhook log status from server * feat: make webhook logs deep linkable * feat: add webhook log pagination * feat: improve webhook request/response body preview
This commit is contained in:
committed by
GitHub
parent
23d0a6b9b2
commit
cfcdc9c98d
@@ -27,4 +27,6 @@ public class WebhookLog
|
||||
public string ResponseBody { get; set; } = string.Empty;
|
||||
|
||||
public bool ExceptionOccured { get; set; }
|
||||
|
||||
public bool IsSuccessStatusCode { get; set; }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class WebhookLogFactory : IWebhookLogFactory
|
||||
Url = webhook.Url,
|
||||
WebhookKey = webhook.Key,
|
||||
RetryCount = retryCount,
|
||||
RequestHeaders = requestMessage.Headers.ToString(),
|
||||
RequestHeaders = $"{requestMessage.Content?.Headers}{requestMessage.Headers}",
|
||||
RequestBody = await requestMessage.Content?.ReadAsStringAsync(cancellationToken)!,
|
||||
ExceptionOccured = exception is not null,
|
||||
};
|
||||
@@ -24,7 +24,8 @@ public class WebhookLogFactory : IWebhookLogFactory
|
||||
if (httpResponseMessage is not null)
|
||||
{
|
||||
log.StatusCode = MapStatusCodeToMessage(httpResponseMessage.StatusCode);
|
||||
log.ResponseHeaders = httpResponseMessage.Headers.ToString();
|
||||
log.IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode;
|
||||
log.ResponseHeaders = $"{httpResponseMessage.Content.Headers}{httpResponseMessage.Headers}";
|
||||
log.ResponseBody = await httpResponseMessage.Content.ReadAsStringAsync(cancellationToken);
|
||||
}
|
||||
else if (exception is HttpRequestException httpRequestException)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Webhooks;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
@@ -33,6 +34,7 @@ internal static class WebhookLogFactory
|
||||
ResponseBody = dto.ResponseBody,
|
||||
RetryCount = dto.RetryCount,
|
||||
StatusCode = dto.StatusCode,
|
||||
IsSuccessStatusCode = Regex.IsMatch(dto.StatusCode, "^.*\\(2(\\d{2})\\)$"),
|
||||
Key = dto.Key,
|
||||
Id = dto.Id,
|
||||
Url = dto.Url,
|
||||
|
||||
@@ -67,6 +67,7 @@ public class WebhookMapDefinition : IMapDefinition
|
||||
target.Url = source.Url;
|
||||
target.RequestHeaders = source.RequestHeaders;
|
||||
target.WebhookKey = source.WebhookKey;
|
||||
target.IsSuccessStatusCode = source.IsSuccessStatusCode;
|
||||
|
||||
if (_hostingEnvironment.IsDebugMode)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,9 @@ public class WebhookLogViewModel
|
||||
[DataMember(Name = "statusCode")]
|
||||
public string StatusCode { get; set; } = string.Empty;
|
||||
|
||||
[DataMember(Name = "isSuccessStatusCode")]
|
||||
public bool IsSuccessStatusCode { get; set; }
|
||||
|
||||
[DataMember(Name = "date")]
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
const vm = this;
|
||||
|
||||
vm.pagination = {
|
||||
pageNumber: 1,
|
||||
pageSize: 25
|
||||
};
|
||||
|
||||
vm.logs = [];
|
||||
vm.openLogOverlay = openLogOverlay;
|
||||
vm.isChecked = isChecked;
|
||||
|
||||
function init() {
|
||||
vm.loading = true;
|
||||
@@ -22,9 +26,14 @@
|
||||
}
|
||||
|
||||
function loadLogs() {
|
||||
return webhooksResource.getLogs()
|
||||
const take = vm.pagination.pageSize;
|
||||
const skip = (vm.pagination.pageNumber - 1) * take;
|
||||
|
||||
return webhooksResource.getLogs(skip, take)
|
||||
.then(data => {
|
||||
vm.logs = data.items;
|
||||
vm.pagination.totalPages = Math.ceil(data.totalItems/vm.pagination.pageSize);
|
||||
|
||||
vm.logs.forEach(log => {
|
||||
formatDatesToLocal(log);
|
||||
});
|
||||
@@ -54,9 +63,16 @@
|
||||
editorService.open(dialog);
|
||||
}
|
||||
|
||||
function isChecked(log) {
|
||||
return log.statusCode === "OK (200)";
|
||||
}
|
||||
vm.previousPage = () => vm.goToPage(vm.pagination.pageNumber - 1);
|
||||
vm.nextPage = () => vm.goToPage(vm.pagination.pageNumber + 1);
|
||||
|
||||
vm.goToPage = (pageNumber) => {
|
||||
vm.pagination.pageNumber = pageNumber;
|
||||
vm.loading = true;
|
||||
loadLogs().then(() => {
|
||||
vm.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><localize key="webhooks_webhookKey">Webhook key</localize></th>
|
||||
<th><localize key="general_date">Date</localize></th>
|
||||
<th><localize key="webhooks_url">Url</localize></th>
|
||||
<th><localize key="webhooks_event">Event</localize></th>
|
||||
@@ -14,13 +13,13 @@
|
||||
<tr ng-repeat="log in vm.logs track by log.key" ng-click="vm.openLogOverlay(log)" style="cursor: pointer;">
|
||||
<td style="width: 20px;">
|
||||
<umb-checkmark
|
||||
ng-if="vm.isChecked(log)"
|
||||
checked="vm.isChecked(log)"
|
||||
size="m">
|
||||
ng-if="log.isSuccessStatusCode"
|
||||
checked="true"
|
||||
size="m"
|
||||
title="{{ log.statusCode }}">
|
||||
</umb-checkmark>
|
||||
<umb-icon icon="icon-wrong" class="umb-checkmark umb-checkmark--m" style="cursor: default;" ng-if="!vm.isChecked(log)"></umb-icon>
|
||||
<umb-icon icon="icon-wrong" class="umb-checkmark umb-checkmark--m" style="cursor: default;" ng-if="!log.isSuccessStatusCode" title="{{ log.statusCode }}"></umb-icon>
|
||||
</td>
|
||||
<td>{{ log.webhookKey }}</td>
|
||||
<td>{{ log.formattedLogDate }}</td>
|
||||
<td>{{ log.url }}</td>
|
||||
<td>{{ log.eventAlias }}</td>
|
||||
@@ -28,4 +27,16 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<umb-empty-state ng-hide="vm.logs.length" position="center">
|
||||
<localize key="content_listViewNoItems">There are no items show in the list.</localize>
|
||||
</umb-empty-state>
|
||||
|
||||
<umb-pagination
|
||||
page-number="vm.pagination.pageNumber"
|
||||
total-pages="vm.pagination.totalPages"
|
||||
on-next="vm.nextPage"
|
||||
on-prev="vm.previousPage"
|
||||
on-go-to-page="vm.goToPage">
|
||||
</umb-pagination>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
vm.close = close;
|
||||
vm.formatData = formatData;
|
||||
vm.detectLanguage = detectLanguage;
|
||||
|
||||
function formatData(data) {
|
||||
|
||||
@@ -12,7 +13,7 @@
|
||||
|
||||
if (data.detectIsJson()) {
|
||||
try {
|
||||
obj = Utilities.fromJson(data)
|
||||
obj = JSON.stringify(Utilities.fromJson(data), null, 2);
|
||||
} catch (err) {
|
||||
obj = data;
|
||||
}
|
||||
@@ -21,6 +22,21 @@
|
||||
return obj;
|
||||
}
|
||||
|
||||
function detectLanguage(headers, defaultLanguage) {
|
||||
const matches = headers.match(/^Content-Type:\s*(?<type>[a-z\/+.-]+)(\;?.*?)$/mi)
|
||||
if (matches) {
|
||||
const contentType = matches.groups["type"];
|
||||
if (contentType === "application/json")
|
||||
return "JSON";
|
||||
if (contentType === "text/html")
|
||||
return "HTML";
|
||||
if (contentType === "application/xml" || contentType === "text/xml")
|
||||
return "XML";
|
||||
}
|
||||
|
||||
return defaultLanguage || "TEXT";
|
||||
}
|
||||
|
||||
function close() {
|
||||
if ($scope.model && $scope.model.close) {
|
||||
$scope.model.close();
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
<div class="flex items-center">
|
||||
<div class="flx-g0 flx-s0" style="flex-basis: 40px;">
|
||||
<umb-checkmark checked="true" size="m" style="cursor: default"
|
||||
ng-if="model.log.statusCode === 'OK (200)'"></umb-checkmark>
|
||||
ng-if="model.log.isSuccessStatusCode"></umb-checkmark>
|
||||
<umb-icon icon="icon-wrong" class="umb-checkmark umb-checkmark--m" style="cursor: default;"
|
||||
ng-if="model.log.statusCode !== 'OK (200)'"></umb-icon>
|
||||
ng-if="!model.log.isSuccessStatusCode"></umb-icon>
|
||||
</div>
|
||||
<div class="flx-g1 flx-s1 flx-b2">{{model.log.statusCode}}</div>
|
||||
</div>
|
||||
@@ -45,6 +45,10 @@
|
||||
<div>{{model.log.retryCount}}</div>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Webhook key">
|
||||
<div>{{model.log.webhookKey}}</div>
|
||||
</umb-control-group>
|
||||
|
||||
</umb-box-content>
|
||||
|
||||
</umb-box>
|
||||
@@ -53,16 +57,14 @@
|
||||
<umb-box-header title="Request"></umb-box-header>
|
||||
<umb-box-content class="block-form">
|
||||
<pre class="code">{{model.log.requestHeaders}}</pre>
|
||||
<umb-code-snippet language="'JSON'" wrap="true">{{vm.formatData(model.log.requestBody) | json}}
|
||||
</umb-code-snippet>
|
||||
<umb-code-snippet language="vm.detectLanguage(model.log.requestHeaders, 'JSON')" wrap="true">{{vm.formatData(model.log.requestBody)}}</umb-code-snippet>
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
<umb-box>
|
||||
<umb-box-header title="Response"></umb-box-header>
|
||||
<umb-box-content class="block-form">
|
||||
<pre class="code">{{model.log.responseHeaders}}</pre>
|
||||
<umb-code-snippet language="'TEXT'" wrap="true">{{vm.formatData(model.log.responseBody) | json}}
|
||||
</umb-code-snippet>
|
||||
<umb-code-snippet language="vm.detectLanguage(model.log.responseHeaders)" wrap="true">{{vm.formatData(model.log.responseBody)}}</umb-code-snippet>
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
<div ng-if="model.log.exceptionOccured">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
vm.page.name = "";
|
||||
vm.page.navigation = [];
|
||||
|
||||
let webhookUri = $routeParams.method;
|
||||
let webhookUri = $routeParams.id;
|
||||
|
||||
onInit();
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
{
|
||||
"name": vm.page.labels.webhooks,
|
||||
"icon": "icon-webhook",
|
||||
"view": "views/webhooks/webhooks.html",
|
||||
"active": webhookUri === 'overview',
|
||||
"view": !webhookUri ? "views/webhooks/webhooks.html" : null,
|
||||
"active": !webhookUri,
|
||||
"alias": "umbWebhooks",
|
||||
"action": function () {
|
||||
$location.path("/settings/webhooks/overview");
|
||||
@@ -43,11 +43,11 @@
|
||||
{
|
||||
"name": vm.page.labels.logs,
|
||||
"icon": "icon-box-alt",
|
||||
"view": "views/webhooks/logs.html",
|
||||
"view": webhookUri === 'logs' ? "views/webhooks/logs.html" : null,
|
||||
"active": webhookUri === 'logs',
|
||||
"alias": "umbWebhookLogs",
|
||||
"action": function () {
|
||||
$location.path("/settings/webhooks/overview");
|
||||
$location.path("/settings/webhooks/overview/logs");
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user