diff --git a/Samples/HelloWorld/Program.cs b/Samples/HelloWorld/Program.cs index 138f336..9bae2f2 100644 --- a/Samples/HelloWorld/Program.cs +++ b/Samples/HelloWorld/Program.cs @@ -18,7 +18,8 @@ .AddMessageActionsHandler() .AddAppMentionHandler() .AddTeamJoinHandler() - .AddEmojiChangedHandler(); + .AddEmojiChangedHandler() + .AddAppMentionHandler(); var app = builder.Build(); @@ -72,3 +73,13 @@ public Task Handle(EventMetaData eventMetadata, EmojiChang return Task.FromResult(new EventHandledResponse("OK")); } } + +class IThrowExceptions : IHandleAppMentions +{ + public bool ShouldHandle(AppMentionEvent slackEvent) => slackEvent.Text.Contains("throw-exception"); + + public Task Handle(EventMetaData eventMetadata, AppMentionEvent slackEvent) + { + throw new Exception("Quack! Something went wrong!"); + } +} diff --git a/Samples/HelloWorld/test.http b/Samples/HelloWorld/test.http index 6a469e9..1c57c3d 100644 --- a/Samples/HelloWorld/test.http +++ b/Samples/HelloWorld/test.http @@ -1,5 +1,5 @@ # Mimicks a payload slack would send for app_mention events, like "@yourbot dostuff" in this case: -GET http://localhost:1337/ +GET http://localhost:5000/ X-Slack-Request-Timestamp: 12331231 X-Slack-Signature: v0:abc123etcetc @@ -8,14 +8,14 @@ X-Slack-Signature: v0:abc123etcetc "event": { "type": "app_mention", "user": "USRAR1YTV", - "text" : "<@BOT123> dostuff", + "text" : "<@BOT123> dostuff and throw-exception", "channel": "C92QZTVEF" } } ### # Verification request -GET http://localhost:1337 +GET http://localhost:5000 { "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl", diff --git a/source/src/Slackbot.Net.Endpoints/Hosting/IAppBuilderExtensions.cs b/source/src/Slackbot.Net.Endpoints/Hosting/IAppBuilderExtensions.cs index aaae45d..0df48a6 100644 --- a/source/src/Slackbot.Net.Endpoints/Hosting/IAppBuilderExtensions.cs +++ b/source/src/Slackbot.Net.Endpoints/Hosting/IAppBuilderExtensions.cs @@ -12,6 +12,7 @@ public static IApplicationBuilder UseSlackbot(this IApplicationBuilder app, bool app.UseMiddleware(); } + app.UseMiddleware(); app.UseMiddleware(); app.MapWhen(Challenge.ShouldRun, b => b.UseMiddleware()); app.MapWhen(Uninstall.ShouldRun, b => b.UseMiddleware()); diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/AppHomeOpenedEvents.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/AppHomeOpenedEvents.cs index 436c377..4a36d2b 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/AppHomeOpenedEvents.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/AppHomeOpenedEvents.cs @@ -23,16 +23,9 @@ public async Task Invoke(HttpContext context) else { logger.LogInformation($"Handling using {handler.GetType()}"); - try - { - logger.LogInformation($"Handling using {handler.GetType()}"); - var response = await handler.Handle(metadata, appHomeOpenedEvent); - logger.LogInformation(response.Response); - } - catch (Exception e) - { - logger.LogError(e, e.Message); - } + logger.LogInformation($"Handling using {handler.GetType()}"); + var response = await handler.Handle(metadata, appHomeOpenedEvent); + logger.LogInformation(response.Response); } await next(context); diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/AppMentionEvents.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/AppMentionEvents.cs index ff7f823..889b367 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/AppMentionEvents.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/AppMentionEvents.cs @@ -1,3 +1,4 @@ +using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Slackbot.Net.Endpoints.Abstractions; @@ -24,6 +25,8 @@ public async Task Invoke(HttpContext context) ["Slack_Channel"] = appMentionEvent?.Channel, ["Slack_User"] = appMentionEvent?.User }); + ExceptionDispatchInfo capturedException = null; + foreach (var handler in handlers) { try @@ -33,12 +36,14 @@ public async Task Invoke(HttpContext context) } catch (Exception e) { - logger.LogError(e, e.Message); + capturedException = ExceptionDispatchInfo.Capture(e); } } - - context.Response.StatusCode = 200; + if (capturedException != null) + { + capturedException?.Throw(); + } } public static bool ShouldRun(HttpContext ctx) diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/Challenge.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/Challenge.cs index 6549b7d..a2c9195 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/Challenge.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/Challenge.cs @@ -16,7 +16,6 @@ public Challenge(RequestDelegate next, ILogger logger) public async Task Invoke(HttpContext context) { var challenge = context.Items[HttpItemKeys.ChallengeKey]; - _logger.LogInformation($"Handling challenge request. Challenge: {challenge}"); context.Response.StatusCode = 200; context.Response.ContentType = "application/json"; diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/EmojiChangedEvents.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/EmojiChangedEvents.cs index 90afadc..99455e2 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/EmojiChangedEvents.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/EmojiChangedEvents.cs @@ -6,7 +6,9 @@ namespace Slackbot.Net.Endpoints.Middlewares; public class EmojiChangedEvents( +#pragma warning disable CS9113 // Parameter is unread. RequestDelegate next, +#pragma warning restore CS9113 // Parameter is unread. ILogger logger, IEnumerable responseHandlers ) @@ -24,16 +26,9 @@ public async Task Invoke(HttpContext context) else { logger.LogInformation("Handling using {HandlerType}", handler.GetType()); - try - { - logger.LogInformation("Handling using {HandlerType}", handler.GetType()); - var response = await handler.Handle(metadata, emojiChanged); - logger.LogInformation("Handler response: {Response}", response.Response); - } - catch (Exception e) - { - logger.LogError(e, e.Message); - } + logger.LogInformation("Handling using {HandlerType}", handler.GetType()); + var response = await handler.Handle(metadata, emojiChanged); + logger.LogInformation("Handler response: {Response}", response.Response); } context.Response.StatusCode = 200; diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/ErrorHandlingMiddleware.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/ErrorHandlingMiddleware.cs new file mode 100644 index 0000000..24baccb --- /dev/null +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/ErrorHandlingMiddleware.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Slackbot.Net.Endpoints.Middlewares; + +public class ErrorHandlingMiddleware(RequestDelegate next, ILogger logger) +{ + public async Task InvokeAsync(HttpContext context) + { + try + { + await next(context); + } + catch (Exception e) + { + logger.LogError(e, e.Message); + context.Response.Headers.Append("x-slack-no-retry", "1"); + context.Response.StatusCode = 500; + await context.Response.WriteAsync("An error occurred while processing the slack event request. Plz do not retry."); + } + } +} diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/HttpItemsManager.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/HttpItemsManager.cs index d987448..e39e73b 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/HttpItemsManager.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/HttpItemsManager.cs @@ -35,7 +35,7 @@ public async Task Invoke(HttpContext context) else { var metadata = JsonSerializer.Deserialize(body, WebOptions); - if (jObject.RootElement.GetProperty("event") is JsonElement @event) + if (jObject.RootElement.GetProperty("event") is var @event) { var slackEvent = ToEventType(@event, body); context.Items.Add(HttpItemKeys.EventMetadataKey, metadata); @@ -78,7 +78,7 @@ private static SlackEvent ToEventType(JsonElement eventJson, string raw) case EventTypes.TeamJoin: return JsonSerializer.Deserialize(json, WebOptions); case EventTypes.EmojiChanged: - return JsonSerializer.Deserialize(json, WebOptions); + return JsonSerializer.Deserialize(json, WebOptions); default: var unknownSlackEvent = JsonSerializer.Deserialize(json, WebOptions); unknownSlackEvent.RawJson = raw; diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/InteractiveEvents.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/InteractiveEvents.cs index d919014..3cb84d6 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/InteractiveEvents.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/InteractiveEvents.cs @@ -59,16 +59,8 @@ private async Task HandleMessageAction(MessageActionInteraction messageAction) else { logger.LogInformation($"Handling using {handler.GetType()}"); - try - { - logger.LogInformation($"Handling using {handler.GetType()}"); - var response = await handler.Handle(messageAction); - logger.LogInformation(response.Response); - } - catch (Exception e) - { - logger.LogError(e, e.Message); - } + var response = await handler.Handle(messageAction); + logger.LogInformation(response.Response); } } diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/MemberJoinedEvents.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/MemberJoinedEvents.cs index 12973c9..9413d8e 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/MemberJoinedEvents.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/MemberJoinedEvents.cs @@ -32,19 +32,9 @@ public async Task Invoke(HttpContext context) else { _logger.LogInformation($"Handling using {handler.GetType()}"); - try - { - _logger.LogInformation($"Handling using {handler.GetType()}"); - var response = await handler.Handle(metadata, memberJoinedChannelEvent); - _logger.LogInformation(response.Response); - } - catch (Exception e) - { - _logger.LogError(e, e.Message); - } + var response = await handler.Handle(metadata, memberJoinedChannelEvent); + _logger.LogInformation(response.Response); } - - context.Response.StatusCode = 200; } public static bool ShouldRun(HttpContext ctx) diff --git a/source/src/Slackbot.Net.Endpoints/Middlewares/TeamJoinEvents.cs b/source/src/Slackbot.Net.Endpoints/Middlewares/TeamJoinEvents.cs index f7f0c81..7a6d00b 100644 --- a/source/src/Slackbot.Net.Endpoints/Middlewares/TeamJoinEvents.cs +++ b/source/src/Slackbot.Net.Endpoints/Middlewares/TeamJoinEvents.cs @@ -6,7 +6,9 @@ namespace Slackbot.Net.Endpoints.Middlewares; public class TeamJoinEvents( +#pragma warning disable CS9113 // Parameter is unread. RequestDelegate next, +#pragma warning restore CS9113 // Parameter is unread. ILogger logger, IEnumerable responseHandlers ) @@ -24,16 +26,8 @@ public async Task Invoke(HttpContext context) else { logger.LogInformation("Handling using {HandlerType}", handler.GetType()); - try - { - logger.LogInformation("Handling using {HandlerType}", handler.GetType()); - var response = await handler.Handle(metadata, teamJoinEvent); - logger.LogInformation("Handler response: {Response}", response.Response); - } - catch (Exception e) - { - logger.LogError(e, e.Message); - } + var response = await handler.Handle(metadata, teamJoinEvent); + logger.LogInformation("Handler response: {Response}", response.Response); } context.Response.StatusCode = 200; diff --git a/source/src/Slackbot.Net.Endpoints/Models/Events/EmojiChangedEvent.cs b/source/src/Slackbot.Net.Endpoints/Models/Events/EmojiChangedEvent.cs index 7071af1..a95df22 100644 --- a/source/src/Slackbot.Net.Endpoints/Models/Events/EmojiChangedEvent.cs +++ b/source/src/Slackbot.Net.Endpoints/Models/Events/EmojiChangedEvent.cs @@ -11,15 +11,15 @@ public class EmojiChangedEvent : SlackEvent public string SubType { get; init; } = string.Empty; // subtype: add, remove, rename public string? Name { get; set; } // subtype: add public string[] Names { get; set; } = []; //subtype: remove - public string OldName { get; set; } // subtype: rename - public string NewName { get; set; } // subtype: rename + public string? OldName { get; set; } // subtype: rename + public string? NewName { get; set; } // subtype: rename public Uri? Value { get; set; }// subtypes: add, rename public EmojiChange CreateSubType() => SubType switch { SubTypeAdd => new EmojiAdded { Name = Name!, Value = Value! }, SubTypeRemove => new EmojiRemoved { Names = Names }, - SubTypeRename => new EmojiRenamed { OldName = OldName, NewName = NewName, Value = Value }, + SubTypeRename => new EmojiRenamed { OldName = OldName!, NewName = NewName!, Value = Value }, _ => new UnknownEmojiChange() }; } @@ -29,7 +29,7 @@ public class EmojiChange(); public class EmojiAdded : EmojiChange { public required string Name { get; init; } - public Uri Value { get; init; } + public required Uri Value { get; init; } } public class EmojiRemoved : EmojiChange @@ -39,9 +39,9 @@ public class EmojiRemoved : EmojiChange public class EmojiRenamed : EmojiChange { - public string OldName { get; set; } // subtype: rename - public string NewName { get; set; } // subtype: rename - public Uri? Value { get; set; }// subtypes: add, rename + public required string OldName { get; init; } // subtype: rename + public required string NewName { get; init; } // subtype: rename + public required Uri? Value { get; init; }// subtypes: add, rename } public class UnknownEmojiChange() : EmojiChange;