Skip to main content

Services

The Infrastructure package provides a suite of cross-cutting services that encapsulate business logic shared across the Alert.CA solution. These services are registered via dedicated extension methods and cover the full alert lifecycle, auditing, external communications, Microsoft Graph integration, notifications, and security.

Registration Overview

Each service has its own extension method — there is no combined registration call for security services.


AlertService

The AlertService orchestrates the full alert lifecycle — from anomaly location creation through subscriber matching and notification dispatch.

Registration

services.AddAlertService(configuration);

Binds NotificationOptions and ESRIOptions, then registers its own repository dependencies internally (ILocationService, ILocationRepository, IAlertSubscriptionRepository, INotificationRepository, IAlertMessageRepository, IUserNotificationRepository, IAlertMessageUserRepository, IAlertMessageAgencyRepository, IAlertMessageLocationRepository, IUserSqlRepository) and IAlertService.

Alert Lifecycle

Key Methods

MethodDescription
CreateAnomalyLocationAsync(ESRIEntity)Create anomaly location from ESRI polygon
CreateAnomalyLocationAsync(PointEntity)Create anomaly location from lat/lon/radius
CreateLocationForSubscriptionAsync(ESRIEntity/PointEntity)Create a location for subscription use (2 overloads)
CreateAlertSubscription(AlertSubscriptionDTO)Create alert subscription
UpdateAlertSubscription(AlertSubscriptionDTO)Update subscription
DeleteSubscriptionById(int)Delete subscription
GetSubscriptionByEntraUserIdAsync(string)Get user's subscriptions with locations
PauseAlertSubscriptions(string, bool)Pause/resume all subscriptions for a user
GetPreDefinedLocations()Get predefined location options grouped by type
CreateAlertMessageAsync(AlertMessageDTO)Full alert creation: anomaly location → alert message → associate recipients
GetAlertMessageById(int)Retrieve alert message by ID
CreateNotificationFromAlertMessage(AlertMessage)Find intersecting subscribers → filter by urgency bitmask and day/time → create notification + user notifications
GetAlertRecipientsByLocationId(int)Get recipient Entra user IDs for a location

Configuration

{
"NotificationOptions": {
"Endpoint": ""
},
"ESRIOptions": {
"AlertEndpoint": "",
"CamerasEndpoint": "",
"PulsePointEndpoint": "",
"ApiKey": "",
"OrgId": "",
"ClientId": "",
"Secret": ""
}
}

AuditService

The AuditService provides structured audit logging with transaction grouping support.

Registration

services.AddAuditService();

No configuration parameter — registers IAuditService as a scoped service. Depends on IAuditEventRepository being available in the container (registered by AddAlertCASQLDatabaseServices).

Key Methods

MethodDescription
CreateSingleAudit(AuditEventCreate audit, string? transactionId)Log a single audit event, auto-generates transaction ID if not provided
CreateTransaction(IEnumerable<AuditEventCreate> audits)Log multiple events under a shared transaction ID

The AuditEventCreate model captures: EntraUserId, EntraDisplayName, EntraEmail, CompanyName, ActionName, ActionDescription, ActionPath, ActionQuery, ActionParameters, ActionNotes, ActionSource.


Communication Services

Two services handle outbound communications via Azure Communication Services:

Registration

services.AddCommunicationServices(configuration);

Registers SmsClient and EmailClient as singletons (created from the CommunicationServicesUrl), then ISmsService and IEmailService as scoped services.

Configuration

{
"CommunicationServicesOptions": {
"CommunicationServicesUrl": "",
"CognitiveServicesUrl": "",
"SendingPhoneNumber": "",
"SendingEmail": ""
}
}

SmsService

Sends SMS notifications with automatic chunking for large recipient lists.

Interface: ISmsService

MethodDescription
SendBulkAsync(IEnumerable<string> phoneNumbers, string message)Send SMS to multiple recipients — automatically chunks lists over 100 for the Azure SDK limit

EmailService

Sends email notifications with support for HTML content, CC/BCC, and file attachments.

Interface: IEmailService

MethodDescription
SendEmailsAsync(subject, body, toUsers, ccUsers, bccUsers, htmlBody?, attachments?)Send email to IEnumerable<EntraUserEntity> recipients
SendEmailsAsync(subject, body, toUsers, ccUsers, bccUsers, htmlBody?, attachments?)Send email to IEnumerable<User> recipients (SQL User model overload)

Both overloads support optional htmlBody (falls back to HTML-encoded plain text) and IEnumerable<EmailAttachmentDto> attachments.

PhoneCallService (Not Auto-Registered)

Makes outbound phone calls via Azure Communication Services CallAutomationClient with Cognitive Services integration for text-to-speech. Not registered by AddCommunicationServices — must be manually registered if needed.

Interface: IPhoneCallService

MethodDescription
CallTargetAsync(string phoneNumber, string hostAppCallbackUri)Initiates a phone call to the target number; the hostAppCallbackUri receives call event callbacks
GetCallConnection(CallAutomationEventBase callEvent)Retrieves the CallConnection for an in-progress call to perform further operations (e.g., play text-to-speech, hang up)

GraphService

The GraphService integrates with Microsoft Graph for Entra ID user directory operations.

Registration

services.AddGraphServices(configuration);

Creates a GraphServiceClient with ClientSecretCredential and registers IGraphService as a scoped service.

Configuration

{
"EntraOptions": {
"TenantId": "",
"ClientId": "",
"ClientSecret": ""
}
}

Key Methods

MethodDescription
GetOneUserById(string entraId)Get single user by Entra Object ID
GetUsersByIds(List<string> entraIds)Get multiple users by IDs (filtered query)
GetAllUsers()Get all users — handles Graph API pagination via OdataNextLink
GetAllCompanies()Get distinct company names across all users
GetUsersByCompanies(List<string> companyNames)Get users filtered by company name — handles pagination

All methods return EntraUserEntity objects (or EntraCompanyEntity for companies). The service selects a fixed set of user properties: id, displayName, givenName, surname, jobTitle, companyName, mail, mobilePhone, officeLocation, preferredLanguage, userPrincipalName, businessPhones.


NotificationService

The NotificationService manages in-app notification retrieval and status updates.

Registration

services.AddNotificationService(configuration);

Binds NotificationOptions and registers INotificationService as a scoped service.

Configuration

{
"NotificationOptions": {
"Endpoint": ""
}
}

Key Methods

MethodDescription
GetNotificationWithRecipientsAsync(int notificationId)Returns NotificationDTO with recipients, sender, and alert message details (lat/lon, map image URL, first image URL)
UpdateNotificationStatus(int notificationId, NotificationStatus newStatus)Update notification status

The NotificationService internally resolves INotificationRepository, IUserNotificationRepository, IUserSqlRepository, and IAlertMessageRepository to assemble the full notification DTO.


ApiKeyValidator

Validates API keys for service-to-service authentication (non-OAuth scenarios, e.g., Azure Function triggers).

Registration

services.AddApiKeyValidator(configuration);

Binds FunctionApiKeyOptions and registers IApiKeyValidator as a scoped service.

Configuration

{
"FunctionApiKeyOptions": {
"FunctionApiKey": ""
}
}

Key Methods

MethodDescription
ValidateApiKey(HttpRequestData req)Validate x-api-key header from Azure Functions isolated worker requests
ValidateApiKey(HttpRequest req)Validate x-api-key header from ASP.NET Core requests
CreateUnauthorizedResponse(HttpRequestData req)Create a 401 Unauthorized response for Azure Functions

JWTService

The JWTService manages OAuth2/OIDC authorization flows using Azure AD B2C CIAM (alertcalifornia.ciamlogin.com) with certificate-based client assertions.

Registration

services.AddJWTService(configuration);

Registered as a singleton. Also registers HttpClient via AddHttpClient().

Configuration

{
"JWTOptions": {
"TenantId": "",
"FrontendRedirectUri": "",
"FrontendClientId": "",
"FrontendAPIClientId": "",
"FrontendCertificatePEM": "",
"FrontendCertificateThumbprint": "",
"AdminRedirectUri": "",
"AdminClientId": "",
"AdminAPIClientId": "",
"AdminCertificatePEM": "",
"AdminCertificateThumbprint": ""
}
}

Key Methods

MethodDescription
GetFrontendAuthorizeRequest()Build OAuth2 authorize URL for frontend app
GetAdminAuthorizeRequest()Build OAuth2 authorize URL for admin app
GetFrontendTokensAsync(string code, string state)Exchange authorization code for tokens (frontend)
GetAdminTokensAsync(string code, string state)Exchange authorization code for tokens (admin)
GetFrontendClientAssertion()Generate RSA-signed JWT client assertion for frontend
GetAdminClientAssertion()Generate RSA-signed JWT client assertion for admin

The service uses certificate-based client authentication — PEM certificates are loaded from configuration, and client assertions are RSA-signed JWTs sent as client_assertion in token requests.


EncryptionService

The EncryptionService provides AES-CBC symmetric encryption with RSA key management via Azure Key Vault.

Registration

services.AddEncryptionService(configuration, credential);

At startup, loads the RSA key from Key Vault and decrypts the stored symmetric key (CameraDataKey) using RSA-OAEP. The decrypted AES key is held in memory for the lifetime of the singleton.

Configuration

{
"KeyVaultOptions": {
"KeyVaultName": "",
"Url": "",
"CameraKeyName": "",
"CameraDataKey": ""
}
}

Encryption Flow

Key Methods

MethodDescription
EncryptAsync(string plainText)Generate random IV → AES-CBC encrypt → prepend IV → return Base64
DecryptAsync(string encryptedText)Extract IV from first 16 bytes → AES-CBC decrypt remainder
SetSymmetricKey()Generate new AES key → encrypt with RSA-OAEP → store in Key Vault as secret