Skip to content

Commit c04418b

Browse files
committed
Logging middleware events and testing with callbacks
1 parent 321a347 commit c04418b

14 files changed

+199
-46
lines changed

src/Deveel.Webhooks.Receiver.AspNetCore/Deveel.Webhooks.Receiver.AspNetCore.xml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/LoggerExtensions.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,44 @@
1818

1919
namespace Deveel.Webhooks {
2020
static partial class LoggerExtensions {
21+
[LoggerMessage(EventId = 10001, Level = LogLevel.Debug,
22+
Message = "A Webhook has arrived")]
23+
public static partial void TraceWebhookArrived(this ILogger logger);
24+
25+
[LoggerMessage(EventId = 10002, Level = LogLevel.Debug,
26+
Message = "A Webhook has been received")]
27+
public static partial void TraceWebhookReceived(this ILogger logger);
28+
29+
[LoggerMessage(EventId = 10003, Level = LogLevel.Debug,
30+
Message = "The webhook of has been handled by '{HandlerType}'")]
31+
public static partial void TraceWebhookHandled(this ILogger logger, Type handlerType);
32+
33+
[LoggerMessage(EventId = 10004, Level = LogLevel.Debug,
34+
Message = "A request of verification has arrived")]
35+
public static partial void TraceVerificationRequest(this ILogger logger);
36+
37+
[LoggerMessage(EventId = 10005, Level = LogLevel.Debug,
38+
Message = "The verification request has been completed successfully")]
39+
public static partial void TraceSuccessVerification(this ILogger logger);
40+
41+
[LoggerMessage(EventId = -20226, Level = LogLevel.Warning,
42+
Message = "The verification request has failed")]
43+
public static partial void WarnVerificationFailed(this ILogger logger);
44+
2145
[LoggerMessage(EventId = -20222, Level = LogLevel.Warning,
22-
Message = "It was not possible to resolve any webhook receiver for the type '{WebhookType}'")]
23-
public static partial void WarnReceiverNotRegistered(this ILogger logger, Type webhookType);
46+
Message = "It was not possible to resolve any webhook receiver")]
47+
public static partial void WarnReceiverNotRegistered(this ILogger logger);
48+
49+
[LoggerMessage(EventId = -20225, Level = LogLevel.Warning,
50+
Message = "It was not possible to verify the signature of the webhook")]
51+
public static partial void WarnInvalidSignature(this ILogger logger);
52+
53+
[LoggerMessage(EventId = -20224, Level = LogLevel.Warning,
54+
Message = "The received webhook is invalid")]
55+
public static partial void WarnInvalidWebhook(this ILogger logger);
56+
57+
[LoggerMessage(EventId = -20223, Level = LogLevel.Error,
58+
Message = "It was not possible to receive a webhook for an unhandled error")]
59+
public static partial void LogUnhandledReceiveError(this ILogger logger, Exception error);
2460
}
2561
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookDelegatedReceiverMiddleware.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,40 @@ public async Task InvokeAsync(HttpContext context) {
5353
var logger = GetLogger(context);
5454

5555
try {
56+
logger.TraceWebhookArrived();
57+
5658
var receiver = context.RequestServices.GetService<IWebhookReceiver<TWebhook>>();
5759

5860
WebhookReceiveResult<TWebhook>? result = null;
5961

6062
if (receiver != null) {
6163
result = await receiver.ReceiveAsync(context.Request, context.RequestAborted);
6264

63-
if (result != null && result.Value.Successful && result.Value.Webhook != null) {
64-
if (asyncHandler != null) {
65-
await asyncHandler(context, result.Value.Webhook, context.RequestAborted);
66-
} else if (syncHandler != null) {
67-
syncHandler(context, result.Value.Webhook);
65+
if (result?.Successful ?? false) {
66+
var webhook = result?.Webhook;
67+
68+
if (webhook == null) {
69+
logger.WarnInvalidWebhook();
70+
} else {
71+
logger.TraceWebhookReceived();
72+
73+
if (asyncHandler != null) {
74+
await asyncHandler(context, webhook, context.RequestAborted);
75+
76+
logger.TraceWebhookHandled(typeof(Func<TWebhook, CancellationToken, Task>));
77+
} else if (syncHandler != null) {
78+
syncHandler(context, webhook);
79+
80+
logger.TraceWebhookHandled(typeof(Action<TWebhook>));
81+
}
6882
}
83+
} else {
84+
logger.WarnInvalidWebhook();
6985
}
86+
} else if (result?.SignatureFailed ?? false) {
87+
logger.WarnInvalidSignature();
7088
} else {
71-
logger.WarnReceiverNotRegistered(typeof(TWebhook));
89+
logger.WarnReceiverNotRegistered();
7290
}
7391

7492
await next.Invoke(context);
@@ -84,17 +102,14 @@ public async Task InvokeAsync(HttpContext context) {
84102
await context.Response.WriteAsync("");
85103
}
86104
} catch (WebhookReceiverException ex) {
87-
// TODO: log this error ...
105+
logger.LogUnhandledReceiveError(ex);
88106

89107
if (!context.Response.HasStarted) {
90108
context.Response.StatusCode = options?.ErrorStatusCode ?? 500;
109+
// TODO: should we emit anything here?
91110
await context.Response.WriteAsync("");
92111
}
93-
94-
// TODO: should we emit anything here?
95112
}
96-
97-
98113
}
99114
}
100115
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookReceiveResult.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,10 @@ public static implicit operator WebhookReceiveResult<TWebhook>(TWebhook? webhook
7171
/// Gets whether the webhook was successfully received.
7272
/// </summary>
7373
public bool Successful => Webhook != null && (!SignatureValidated || SignatureValid == true);
74+
75+
/// <summary>
76+
/// Gets whether the webhook was received but the signature was invalid.
77+
/// </summary>
78+
public bool SignatureFailed => SignatureValidated && SignatureValid == false;
7479
}
7580
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookReceiverBuilder.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ public WebhookReceiverBuilder()
6969
public IServiceCollection Services { get; }
7070

7171
private void RegisterReceiverMiddleware() {
72-
Services.AddScoped<WebhookReceiverMiddleware<TWebhook>>();
72+
Services.TryAddScoped<WebhookReceiverMiddleware<TWebhook>>();
7373
}
7474

7575
private void RegisterVerifierMiddleware() {
76-
Services.AddScoped<WebhookRequestVerfierMiddleware<TWebhook>>();
76+
Services.TryAddScoped<WebhookRequestVerfierMiddleware<TWebhook>>();
7777
}
7878

7979
private void RegisterDefaultReceiver() {
@@ -272,8 +272,8 @@ public WebhookReceiverBuilder<TWebhook> UseJsonParser<TParser>(TParser parser)
272272
/// Returns the current builder instance with the parser registered
273273
/// </returns>
274274
public WebhookReceiverBuilder<TWebhook> UseJsonParser(JsonSerializerOptions? options = null) {
275-
Services.AddSingleton<IWebhookJsonParser<TWebhook>>(_ => new SystemTextWebhookJsonParser<TWebhook>(options));
276-
Services.AddSingleton(_ => new SystemTextWebhookJsonParser<TWebhook>(options));
275+
Services.TryAddSingleton<IWebhookJsonParser<TWebhook>>(_ => new SystemTextWebhookJsonParser<TWebhook>(options));
276+
Services.TryAddSingleton(_ => new SystemTextWebhookJsonParser<TWebhook>(options));
277277

278278
return this;
279279
}
@@ -294,7 +294,7 @@ public WebhookReceiverBuilder<TWebhook> UseJsonParser(Func<Stream, CancellationT
294294
if (parser is null)
295295
throw new ArgumentNullException(nameof(parser));
296296

297-
Services.AddSingleton<IWebhookJsonParser<TWebhook>>(_ => new DelegatedJsonParser(parser));
297+
Services.TryAddSingleton<IWebhookJsonParser<TWebhook>>(_ => new DelegatedJsonParser(parser));
298298

299299
return this;
300300
}
@@ -315,7 +315,7 @@ public WebhookReceiverBuilder<TWebhook> UseJsonParser(Func<string, TWebhook> par
315315
if (parser is null)
316316
throw new ArgumentNullException(nameof(parser));
317317

318-
Services.AddSingleton<IWebhookJsonParser<TWebhook>>(_ => new DelegatedJsonParser(parser));
318+
Services.TryAddSingleton<IWebhookJsonParser<TWebhook>>(_ => new DelegatedJsonParser(parser));
319319

320320
return this;
321321
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookReceiverMiddleware.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,23 @@ public WebhookReceiverMiddleware(
4343

4444
public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
4545
try {
46+
logger.TraceWebhookArrived();
47+
4648
var result = await receiver.ReceiveAsync(context.Request, context.RequestAborted);
47-
49+
50+
if (result.Successful) {
51+
logger.TraceWebhookReceived();
52+
} else if (result.SignatureFailed) {
53+
logger.WarnInvalidSignature();
54+
} else if (!result.Successful) {
55+
logger.WarnInvalidWebhook();
56+
}
57+
4858
if (handlers != null && result.Successful && result.Webhook != null) {
4959
foreach (var handler in handlers) {
5060
await handler.HandleAsync(result.Webhook, context.RequestAborted);
61+
62+
logger.TraceWebhookHandled(handler.GetType());
5163
}
5264
}
5365

@@ -56,7 +68,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
5668
if (!context.Response.HasStarted) {
5769
if (result.Successful) {
5870
context.Response.StatusCode = SuccessStatusCode;
59-
} else if (result.SignatureValidated && !result.SignatureValid.Value) {
71+
} else if (result.SignatureFailed) {
6072
context.Response.StatusCode = InvalidStatusCode;
6173
} else {
6274
context.Response.StatusCode = FailureStatusCode;
@@ -66,16 +78,14 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
6678
await context.Response.WriteAsync("");
6779
}
6880
} catch (WebhookReceiverException ex) {
69-
// TODO: log this error ...
81+
logger.LogUnhandledReceiveError(ex);
7082

7183
if (!context.Response.HasStarted) {
7284
context.Response.StatusCode = FailureStatusCode;
7385

7486
// TODO: should we emit anything here?
7587
await context.Response.WriteAsync("");
7688
}
77-
78-
// TODO: should we emit anything here?
7989
}
8090

8191
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookRequestVerfierMiddleware.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,35 @@ public WebhookRequestVerfierMiddleware(
3535
this.logger = logger ?? NullLogger<WebhookRequestVerfierMiddleware<TWebhook>>.Instance;
3636
}
3737

38+
private int FailureStatusCode => options.ErrorStatusCode ?? 500;
39+
3840
public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
3941
try {
42+
logger.TraceVerificationRequest();
43+
4044
var result = await requestVerifier.VerifyRequestAsync(context.Request, context.RequestAborted);
4145

46+
if (result != null) {
47+
if (result.IsVerified) {
48+
logger.TraceSuccessVerification();
49+
} else {
50+
logger.WarnVerificationFailed();
51+
}
52+
}
53+
4254
await next.Invoke(context);
4355

44-
if (!context.Response.HasStarted) {
56+
if (!context.Response.HasStarted && result != null) {
4557
await requestVerifier.HandleResultAsync(result, context.Response, context.RequestAborted);
4658
}
4759
} catch (WebhookReceiverException ex) {
60+
logger.LogUnhandledReceiveError(ex);
61+
4862
if (!context.Response.HasStarted) {
49-
63+
context.Response.StatusCode = FailureStatusCode;
64+
65+
// TODO: Should we emit anything here?
66+
await context.Response.WriteAsync("");
5067
}
5168
}
5269
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Deveel.Webhooks.Handlers {
2+
public interface IWebhookCallback<TWebhook> {
3+
void OnWebhookHandled(TWebhook? webhook);
4+
}
5+
}

test/Deveel.Webhooks.Receiver.TestApi/Handlers/TestSignedWebhookHandler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
namespace Deveel.Webhooks.Handlers {
66
public class TestSignedWebhookHandler : IWebhookHandler<TestSignedWebhook> {
7-
private readonly ILogger<TestSignedWebhookHandler> _logger;
7+
private readonly IWebhookCallback<TestSignedWebhook> _callback;
88

9-
public TestSignedWebhookHandler(ILogger<TestSignedWebhookHandler> logger) {
10-
_logger = logger;
9+
public TestSignedWebhookHandler(IWebhookCallback<TestSignedWebhook> callback) {
10+
_callback = callback;
1111
}
1212

1313
public Task HandleAsync(TestSignedWebhook webhook, CancellationToken cancellationToken = default) {
14-
_logger.LogInformation(JsonConvert.SerializeObject(webhook));
14+
_callback.OnWebhookHandled(webhook);
1515

1616
return Task.CompletedTask;
1717
}

test/Deveel.Webhooks.Receiver.TestApi/Handlers/TestWebhookHandler.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@
22

33
using Microsoft.Extensions.Options;
44

5-
using Newtonsoft.Json;
6-
75
namespace Deveel.Webhooks.Handlers {
86
public class TestWebhookHandler : IWebhookHandler<TestWebhook> {
9-
private readonly ILogger<TestWebhookHandler> _logger;
107
private readonly WebhookReceiverOptions options;
8+
private readonly IWebhookCallback<TestWebhook> callback;
119

12-
public TestWebhookHandler(IOptionsSnapshot<WebhookReceiverOptions> options, ILogger<TestWebhookHandler> logger) {
13-
_logger = logger;
14-
this.options = options.Get(nameof(TestWebhook));
10+
public TestWebhookHandler(IOptionsSnapshot<WebhookReceiverOptions> options, IWebhookCallback<TestWebhook> callback) {
11+
this.options = options.GetReceiverOptions<TestWebhook>();
12+
this.callback = callback;
1513
}
1614

1715
public Task HandleAsync(TestWebhook webhook, CancellationToken cancellationToken = default) {
18-
_logger.LogInformation(JsonConvert.SerializeObject(webhook));
19-
16+
callback.OnWebhookHandled(webhook);
2017
return Task.CompletedTask;
2118
}
2219
}

0 commit comments

Comments
 (0)