mirror of
https://github.com/bitwarden/server.git
synced 2025-12-10 00:42:07 -06:00
Event integration updates and cleanups (#6288)
* Event integration updates and cleanups * Fix empty message on ArgumentException * Adjust exception message Co-authored-by: Matt Bishop <mbishop@bitwarden.com> --------- Co-authored-by: Matt Bishop <mbishop@bitwarden.com>
This commit is contained in:
parent
7e50a46d3b
commit
0fbbb6a984
@ -28,12 +28,12 @@ public abstract class EventLoggingListenerService : BackgroundService
|
||||
if (root.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var eventMessages = root.Deserialize<IEnumerable<EventMessage>>();
|
||||
await _handler.HandleManyEventsAsync(eventMessages);
|
||||
await _handler.HandleManyEventsAsync(eventMessages ?? throw new JsonException("Deserialize returned null"));
|
||||
}
|
||||
else if (root.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var eventMessage = root.Deserialize<EventMessage>();
|
||||
await _handler.HandleEventAsync(eventMessage);
|
||||
await _handler.HandleEventAsync(eventMessage ?? throw new JsonException("Deserialize returned null"));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
@ -20,8 +19,56 @@ public abstract class IntegrationHandlerBase<T> : IIntegrationHandler<T>
|
||||
public async Task<IntegrationHandlerResult> HandleAsync(string json)
|
||||
{
|
||||
var message = IntegrationMessage<T>.FromJson(json);
|
||||
return await HandleAsync(message);
|
||||
return await HandleAsync(message ?? throw new ArgumentException("IntegrationMessage was null when created from the provided JSON"));
|
||||
}
|
||||
|
||||
public abstract Task<IntegrationHandlerResult> HandleAsync(IntegrationMessage<T> message);
|
||||
|
||||
protected IntegrationHandlerResult ResultFromHttpResponse(
|
||||
HttpResponseMessage response,
|
||||
IntegrationMessage<T> message,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
var result = new IntegrationHandlerResult(success: response.IsSuccessStatusCode, message);
|
||||
|
||||
if (response.IsSuccessStatusCode) return result;
|
||||
|
||||
switch (response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.TooManyRequests:
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
case HttpStatusCode.InternalServerError:
|
||||
case HttpStatusCode.BadGateway:
|
||||
case HttpStatusCode.ServiceUnavailable:
|
||||
case HttpStatusCode.GatewayTimeout:
|
||||
result.Retryable = true;
|
||||
result.FailureReason = response.ReasonPhrase ?? $"Failure with status code: {(int)response.StatusCode}";
|
||||
|
||||
if (response.Headers.TryGetValues("Retry-After", out var values))
|
||||
{
|
||||
var value = values.FirstOrDefault();
|
||||
if (int.TryParse(value, out var seconds))
|
||||
{
|
||||
// Retry-after was specified in seconds. Adjust DelayUntilDate by the requested number of seconds.
|
||||
result.DelayUntilDate = timeProvider.GetUtcNow().AddSeconds(seconds).UtcDateTime;
|
||||
}
|
||||
else if (DateTimeOffset.TryParseExact(value,
|
||||
"r", // "r" is the round-trip format: RFC1123
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||
out var retryDate))
|
||||
{
|
||||
// Retry-after was specified as a date. Adjust DelayUntilDate to the specified date.
|
||||
result.DelayUntilDate = retryDate.UtcDateTime;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.Retryable = false;
|
||||
result.FailureReason = response.ReasonPhrase ?? $"Failure with status code {(int)response.StatusCode}";
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,13 +418,21 @@ dependencies and integrations. For instance, `SlackIntegrationHandler` needs a `
|
||||
`AddEventIntegrationServices` has a call to `AddSlackService`. Same thing for webhooks when it
|
||||
comes to defining a custom HttpClient by name.
|
||||
|
||||
1. In `AddEventIntegrationServices` create the listener configuration:
|
||||
In `AddEventIntegrationServices`:
|
||||
|
||||
1. Create the singleton for the handler:
|
||||
|
||||
``` csharp
|
||||
services.TryAddSingleton<IIntegrationHandler<ExampleIntegrationConfigurationDetails>, ExampleIntegrationHandler>();
|
||||
```
|
||||
|
||||
2. Create the listener configuration:
|
||||
|
||||
``` csharp
|
||||
var exampleConfiguration = new ExampleListenerConfiguration(globalSettings);
|
||||
```
|
||||
|
||||
2. Add the integration to both the RabbitMQ and ASB specific declarations:
|
||||
3. Add the integration to both the RabbitMQ and ASB specific declarations:
|
||||
|
||||
``` csharp
|
||||
services.AddRabbitMqIntegration<ExampleIntegrationConfigurationDetails, ExampleListenerConfiguration>(exampleConfiguration);
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||
@ -17,7 +15,8 @@ public class WebhookIntegrationHandler(
|
||||
|
||||
public const string HttpClientName = "WebhookIntegrationHandlerHttpClient";
|
||||
|
||||
public override async Task<IntegrationHandlerResult> HandleAsync(IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
|
||||
public override async Task<IntegrationHandlerResult> HandleAsync(
|
||||
IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, message.Configuration.Uri);
|
||||
request.Content = new StringContent(message.RenderedTemplate, Encoding.UTF8, "application/json");
|
||||
@ -28,45 +27,8 @@ public class WebhookIntegrationHandler(
|
||||
parameter: message.Configuration.Token
|
||||
);
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
var result = new IntegrationHandlerResult(success: response.IsSuccessStatusCode, message);
|
||||
|
||||
switch (response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.TooManyRequests:
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
case HttpStatusCode.InternalServerError:
|
||||
case HttpStatusCode.BadGateway:
|
||||
case HttpStatusCode.ServiceUnavailable:
|
||||
case HttpStatusCode.GatewayTimeout:
|
||||
result.Retryable = true;
|
||||
result.FailureReason = response.ReasonPhrase ?? $"Failure with status code: {(int)response.StatusCode}";
|
||||
|
||||
if (response.Headers.TryGetValues("Retry-After", out var values))
|
||||
{
|
||||
var value = values.FirstOrDefault();
|
||||
if (int.TryParse(value, out var seconds))
|
||||
{
|
||||
// Retry-after was specified in seconds. Adjust DelayUntilDate by the requested number of seconds.
|
||||
result.DelayUntilDate = timeProvider.GetUtcNow().AddSeconds(seconds).UtcDateTime;
|
||||
}
|
||||
else if (DateTimeOffset.TryParseExact(value,
|
||||
"r", // "r" is the round-trip format: RFC1123
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||
out var retryDate))
|
||||
{
|
||||
// Retry-after was specified as a date. Adjust DelayUntilDate to the specified date.
|
||||
result.DelayUntilDate = retryDate.UtcDateTime;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.Retryable = false;
|
||||
result.FailureReason = response.ReasonPhrase ?? $"Failure with status code {(int)response.StatusCode}";
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
return ResultFromHttpResponse(response, message, timeProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using AutoMapper;
|
||||
using AutoMapper;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
||||
|
||||
public class OrganizationIntegration : Core.AdminConsole.Entities.OrganizationIntegration
|
||||
{
|
||||
public virtual Organization Organization { get; set; }
|
||||
public virtual required Organization Organization { get; set; }
|
||||
}
|
||||
|
||||
public class OrganizationIntegrationMapperProfile : Profile
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using AutoMapper;
|
||||
using AutoMapper;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
||||
|
||||
public class OrganizationIntegrationConfiguration : Core.AdminConsole.Entities.OrganizationIntegrationConfiguration
|
||||
{
|
||||
public virtual OrganizationIntegration OrganizationIntegration { get; set; }
|
||||
public virtual required OrganizationIntegration OrganizationIntegration { get; set; }
|
||||
}
|
||||
|
||||
public class OrganizationIntegrationConfigurationMapperProfile : Profile
|
||||
|
||||
@ -51,6 +51,7 @@ public class WebhookIntegrationHandlerTests
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.Equal(result.Message, message);
|
||||
Assert.Empty(result.FailureReason);
|
||||
|
||||
sutProvider.GetDependency<IHttpClientFactory>().Received(1).CreateClient(
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(WebhookIntegrationHandler.HttpClientName))
|
||||
@ -59,6 +60,7 @@ public class WebhookIntegrationHandlerTests
|
||||
Assert.Single(_handler.CapturedRequests);
|
||||
var request = _handler.CapturedRequests[0];
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Content);
|
||||
var returned = await request.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpMethod.Post, request.Method);
|
||||
@ -77,6 +79,7 @@ public class WebhookIntegrationHandlerTests
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.Equal(result.Message, message);
|
||||
Assert.Empty(result.FailureReason);
|
||||
|
||||
sutProvider.GetDependency<IHttpClientFactory>().Received(1).CreateClient(
|
||||
Arg.Is(AssertHelper.AssertPropertyEqual(WebhookIntegrationHandler.HttpClientName))
|
||||
@ -85,6 +88,7 @@ public class WebhookIntegrationHandlerTests
|
||||
Assert.Single(_handler.CapturedRequests);
|
||||
var request = _handler.CapturedRequests[0];
|
||||
Assert.NotNull(request);
|
||||
Assert.NotNull(request.Content);
|
||||
var returned = await request.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpMethod.Post, request.Method);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user