-
Notifications
You must be signed in to change notification settings - Fork 3
Introduce Azure Event Grid Notification Service #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
6319ae4
22a5e3c
fc3a5fd
4554ef8
5778769
c2c50c0
0d10321
8967200
121c67b
4c9cc1a
108857f
6c7d9ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| using Azure.Messaging.EventGrid; | ||
| using Microsoft.Extensions.Logging; | ||
| using sama.Models; | ||
| using System; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace sama.Services; | ||
|
|
||
| public class EventGridNotificationService : INotificationService | ||
| { | ||
| private readonly ILogger<EventGridNotificationService> _logger; | ||
| private readonly SettingsService _settings; | ||
| private readonly BackgroundExecutionWrapper _bgExec; | ||
| private readonly EventGridPublisherClientWrapper _eventGridWrapper; | ||
|
|
||
| public EventGridNotificationService(ILogger<EventGridNotificationService> logger, SettingsService settings, BackgroundExecutionWrapper bgExec, EventGridPublisherClientWrapper eventGridWrapper) | ||
| { | ||
| _logger = logger; | ||
| _settings = settings; | ||
| _bgExec = bgExec; | ||
| _eventGridWrapper = eventGridWrapper; | ||
| } | ||
|
|
||
| private static class EventTypes | ||
|
||
| { | ||
| public const string CheckCompleted = "sama.endpoint.check.completed"; | ||
| public const string StatusUp = "sama.endpoint.status.up"; | ||
| public const string StatusDown = "sama.endpoint.status.down"; | ||
| public const string ManagementAdded = "sama.endpoint.management.added"; | ||
| public const string ManagementRemoved = "sama.endpoint.management.removed"; | ||
| public const string ManagementEnabled = "sama.endpoint.management.enabled"; | ||
| public const string ManagementDisabled = "sama.endpoint.management.disabled"; | ||
| public const string ManagementReconfigured = "sama.endpoint.management.reconfigured"; | ||
| public const string ManagementUnknown = "sama.endpoint.management.unknown"; | ||
| } | ||
|
|
||
| private static string Subject(string path) => $"Sama/{path}"; | ||
| private static string EndpointSubject(string path) => Subject($"endpoints/{path}"); | ||
|
|
||
| public virtual void NotifySingleResult(Endpoint endpoint, EndpointCheckResult result) | ||
| { | ||
| SendEvent( | ||
| EventTypes.CheckCompleted, | ||
| EndpointSubject($"{endpoint.Id}"), | ||
| new | ||
| { | ||
| endpointId = endpoint.Id, | ||
| endpointName = endpoint.Name, | ||
| success = result.Success, | ||
| responseTime = result.ResponseTime?.TotalMilliseconds, | ||
| startTime = result.Start, | ||
| stopTime = result.Stop, | ||
| error = result.Error?.Message | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| public virtual void NotifyUp(Endpoint endpoint, DateTimeOffset? downAsOf) | ||
| { | ||
| var downtimeMinutes = downAsOf.HasValue | ||
| ? (int)DateTimeOffset.UtcNow.Subtract(downAsOf.Value).TotalMinutes | ||
| : 0; | ||
|
|
||
| SendEvent( | ||
| EventTypes.StatusUp, | ||
| EndpointSubject($"{endpoint.Id}"), | ||
| new | ||
| { | ||
| endpointId = endpoint.Id, | ||
| endpointName = endpoint.Name, | ||
| downAsOf = downAsOf, | ||
| downtimeMinutes = downtimeMinutes, | ||
| recoveredAt = DateTimeOffset.UtcNow | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| public virtual void NotifyDown(Endpoint endpoint, DateTimeOffset downAsOf, Exception? reason) | ||
| { | ||
| SendEvent( | ||
| EventTypes.StatusDown, | ||
| EndpointSubject($"{endpoint.Id}"), | ||
| new | ||
| { | ||
| endpointId = endpoint.Id, | ||
| endpointName = endpoint.Name, | ||
| downAsOf = downAsOf, | ||
| reason = reason?.Message, | ||
| reasonType = reason?.GetType().Name | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| public virtual void NotifyMisc(Endpoint endpoint, NotificationType type) | ||
| { | ||
| var eventType = type switch | ||
| { | ||
| NotificationType.EndpointAdded => EventTypes.ManagementAdded, | ||
| NotificationType.EndpointRemoved => EventTypes.ManagementRemoved, | ||
| NotificationType.EndpointEnabled => EventTypes.ManagementEnabled, | ||
| NotificationType.EndpointDisabled => EventTypes.ManagementDisabled, | ||
| NotificationType.EndpointReconfigured => EventTypes.ManagementReconfigured, | ||
| _ => EventTypes.ManagementUnknown | ||
| }; | ||
|
|
||
| SendEvent( | ||
| eventType, | ||
| EndpointSubject($"{endpoint.Id}"), | ||
| new | ||
| { | ||
| endpointId = endpoint.Id, | ||
| endpointName = endpoint.Name, | ||
| notificationType = type.ToString(), | ||
| timestamp = DateTimeOffset.UtcNow | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| private async Task SendEventAsync(string eventType, string subject, object data) | ||
| { | ||
| try | ||
| { | ||
| if (!IsConfigured()) | ||
| { | ||
| _logger.LogDebug("Event Grid notification service is not configured, skipping event"); | ||
| return; | ||
| } | ||
|
|
||
| var topicEndpoint = new Uri(_settings.Notifications_EventGrid_TopicEndpoint!); | ||
| var accessKey = _settings.Notifications_EventGrid_AccessKey!; | ||
|
|
||
| var eventGridEvent = new EventGridEvent( | ||
| subject: subject, | ||
| eventType: eventType, | ||
| dataVersion: "1.0", | ||
| data: BinaryData.FromObjectAsJson(data) | ||
| ); | ||
|
|
||
| await _eventGridWrapper.SendEventAsync(topicEndpoint, accessKey, eventGridEvent); | ||
|
|
||
| _logger.LogDebug("Successfully sent Event Grid event: {EventType} for {Subject}", eventType, subject); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Failed to send Event Grid notification for event type {EventType} and subject {Subject}", eventType, subject); | ||
| } | ||
| } | ||
|
|
||
| private void SendEvent(string eventType, string subject, object data) | ||
| { | ||
| _bgExec.Execute(() => SendEventAsync(eventType, subject, data).Wait()); | ||
| } | ||
|
|
||
| private bool IsConfigured() | ||
| { | ||
| return !string.IsNullOrWhiteSpace(_settings.Notifications_EventGrid_TopicEndpoint) | ||
| && !string.IsNullOrWhiteSpace(_settings.Notifications_EventGrid_AccessKey); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| using Azure; | ||
| using Azure.Messaging.EventGrid; | ||
| using System; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace sama.Services; | ||
|
|
||
| /// <summary> | ||
| /// This is a wrapper around EventGridPublisherClient functionality that cannot be (easily) tested. | ||
| /// </summary> | ||
| [ExcludeFromCodeCoverage] | ||
| public class EventGridPublisherClientWrapper | ||
| { | ||
| public virtual async Task SendEventAsync(Uri topicEndpoint, string accessKey, EventGridEvent eventGridEvent) | ||
| { | ||
| if (topicEndpoint == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(topicEndpoint), "Topic endpoint is not configured"); | ||
| } | ||
|
|
||
| if (string.IsNullOrWhiteSpace(accessKey)) | ||
| { | ||
| throw new ArgumentNullException(nameof(accessKey), "Access key is not configured"); | ||
| } | ||
|
|
||
| var credential = new AzureKeyCredential(accessKey); | ||
| var client = new EventGridPublisherClient(topicEndpoint, credential); | ||
|
|
||
| await client.SendEventAsync(eventGridEvent); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: spacing was not reformatted after switching
namespacestyleThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will def fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pushed an update.