From 10c44b19a0ee63a4746bcd026ac4dc1ba928e0a8 Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Fri, 14 Aug 2026 12:55:38 +0800 Subject: [PATCH 1/3] Enable nullable annotations in ServiceControl.Persistence --- .../Implementation/BodyStorage/BodyStorage.cs | 2 +- .../Implementation/EventLogDataStore.cs | 2 +- .../FailedErrorImportDataStore.cs | 6 ++-- .../Implementation/FailedMessageViewMapper.cs | 12 +++---- .../Implementation/GroupsDataStore.cs | 14 ++++---- .../Implementation/RetryBatchStore.cs | 2 +- .../Implementation/RetryHistoryDataStore.cs | 4 +-- .../EndpointDetailsParser.cs | 4 +-- .../CompositeViews/MessagesViewTests.cs | 2 ++ .../FailedMessageBuilder.cs | 1 + .../EFCore/PersistenceTestsContext.cs | 3 +- .../MessageRedirectsDataStoreTests.cs | 4 +-- .../RetryConfirmationProcessorTests.cs | 3 +- src/ServiceControl.Persistence/CustomCheck.cs | 10 +++--- .../CustomCheckDetail.cs | 10 +++--- .../EmailNotifications.cs | 10 +++--- .../EndpointDetails.cs | 6 ++-- .../EndpointInstanceId.cs | 4 +-- .../EndpointSettings.cs | 2 +- .../EndpointsView.cs | 6 ++-- .../EventLog/EventLogItem.cs | 8 ++--- .../EventLog/EventLogItemView.cs | 10 +++--- .../ExceptionDetails.cs | 8 ++--- .../ExternalIntegrationDispatchRequest.cs | 4 +-- .../FailedErrorImport.cs | 6 ++-- .../FailedMessage.cs | 20 +++++------ .../FailedMessageView.cs | 16 ++++----- .../FailedTransportMessage.cs | 6 ++-- .../FailureDetails.cs | 4 +-- .../FailureGroupMessageView.cs | 10 +++--- .../FailureGroupView.cs | 8 ++--- .../ForwardingRetryBatch.cs | 2 +- .../GroupOperation.cs | 10 +++--- .../History/HistoricRetryOperation.cs | 4 +-- .../History/UnacknowledgedRetryOperation.cs | 6 ++-- .../IBodyStorage.cs | 8 ++--- .../ICustomChecksDataStore.cs | 2 +- .../IEventLogDataStore.cs | 2 +- .../IGroupsDataStore.cs | 10 +++--- .../IMessagesViewDataStore.cs | 8 ++--- .../IRetryBatchStore.cs | 8 ++--- .../IRetryHistoryDataStore.cs | 2 +- .../Infrastructure/DateTimeRange.cs | 2 +- .../Infrastructure/DeterministicGuid.cs | 15 ++------ .../Infrastructure/QueryResult.cs | 6 ++-- .../Infrastructure/SortInfo.cs | 2 +- .../TransportMessageExtensions.cs | 18 +++++----- .../KnownEndpoint.cs | 4 +-- .../KnownEndpointsView.cs | 4 +-- .../MessageRedirects/MessageRedirect.cs | 6 ++-- .../MessageRedirectExtensions.cs | 4 +-- .../MessagesView.cs | 22 ++++++------ .../PersistenceManifest.cs | 36 ++++++++++--------- .../PersistenceSettings.cs | 2 +- .../ProcessedMessage.cs | 4 +-- .../QueueAddress.cs | 2 +- .../Archiving/IArchiveMessages.cs | 4 +-- .../Archiving/InMemoryArchive.cs | 2 +- .../Archiving/InMemoryUnarchive.cs | 2 +- .../Archiving/OperationsManager.cs | 8 +++-- .../ClassifiableMessageDetails.cs | 2 +- src/ServiceControl.Persistence/RetryBatch.cs | 18 +++++----- .../RetryBatchGroup.cs | 6 ++-- .../ServiceControl.Persistence.csproj | 1 + .../UnitOfWork/FallbackIngestionUnitOfWork.cs | 28 +++++++-------- .../UnitOfWork/IIngestionUnitOfWork.cs | 4 +-- .../UnitOfWork/IngestionUnitOfWorkBase.cs | 4 +-- .../MessageFailedConverterTests.cs | 5 +-- .../MessageFailures/ArchiveScopeAuditTests.cs | 3 +- .../AsyncRangeAndQueueAuditTests.cs | 9 +++-- .../EditFailedMessagesControllerAuditTests.cs | 2 +- ...tEndpointSettingsSyncHostedServiceTests.cs | 4 +-- .../Operations/EndpointDetailsParser.cs | 4 +-- 73 files changed, 253 insertions(+), 249 deletions(-) diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage/BodyStorage.cs b/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage/BodyStorage.cs index cdfdda6b4e..d974cbb51f 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage/BodyStorage.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage/BodyStorage.cs @@ -60,7 +60,7 @@ public async Task TryFetch(string bodyId, CancellationToken c return MessageBodyResult.Available(new MessageBodyStreamContent( new MemoryStream(bytes, writable: false), - row.BodyContentType, + row.BodyContentType ?? "text/plain", bytes.Length, uniqueMessageId)); } diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs index ffc0efa4e7..82ec32b9d9 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs @@ -13,7 +13,7 @@ public Task Add(EventLogItem logItem, CancellationToken cancellationToken = defa { dbContext.EventLogItems.Add(new EventLogItemEntity { - Description = logItem.Description, + Description = logItem.Description ?? "", Severity = logItem.Severity, RaisedAt = logItem.RaisedAt, RelatedTo = logItem.RelatedTo ?? [], diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs index bfdfbbc09f..be1c95ffc1 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs @@ -32,7 +32,7 @@ public Task QueryContainsFailedImports(CancellationToken cancellationToken public Task StoreFailedErrorImport(FailedErrorImport failure, CancellationToken cancellationToken = default) => ExecuteWithDbContext(async (dbContext, token) => { - var uniqueMessageId = FailedErrorImport.DeriveKey(failure.Message.Headers, failure.Message.Id); + var uniqueMessageId = FailedErrorImport.DeriveKey(failure.Message!.Headers, failure.Message.Id); var body = failure.Message.Body ?? []; var storeExternally = body.Length > bodyStorageSettings.MaxBodySizeToStore; @@ -54,7 +54,7 @@ public Task StoreFailedErrorImport(FailedErrorImport failure, CancellationToken HeadersJson = headersJson, Body = storedBody, BodyStoredExternally = storeExternally, - ExceptionInfo = failure.ExceptionInfo + ExceptionInfo = failure.ExceptionInfo ?? "" }, (entity) => { entity.FailedAt = failedAt; @@ -62,7 +62,7 @@ public Task StoreFailedErrorImport(FailedErrorImport failure, CancellationToken entity.HeadersJson = headersJson; entity.Body = storedBody; entity.BodyStoredExternally = storeExternally; - entity.ExceptionInfo = failure.ExceptionInfo; + entity.ExceptionInfo = failure.ExceptionInfo ?? ""; }, token); }, cancellationToken); diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs index 418f17eba0..f50af192bd 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs @@ -127,22 +127,22 @@ static ExceptionDetails ToExceptionDetails(this FailedMessageEntity entity, Dict }; public static EndpointDetails? ToSendingEndpoint(this FailedMessageEntity entity) => - entity.SendingEndpointName == null + entity.SendingEndpointName == null && entity.SendingEndpointHost == null ? null : new EndpointDetails { - Name = entity.SendingEndpointName, - Host = entity.SendingEndpointHost, + Name = entity.SendingEndpointName ?? "", + Host = entity.SendingEndpointHost ?? "", HostId = entity.SendingEndpointHostId ?? Guid.Empty }; public static EndpointDetails? ToReceivingEndpoint(this FailedMessageEntity entity) => - entity.ReceivingEndpointName == null + entity.ReceivingEndpointName == null && entity.ReceivingEndpointHost == null ? null : new EndpointDetails { - Name = entity.ReceivingEndpointName, - Host = entity.ReceivingEndpointHost, + Name = entity.ReceivingEndpointName ?? "", + Host = entity.ReceivingEndpointHost ?? "", HostId = entity.ReceivingEndpointHostId ?? Guid.Empty }; diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs index 7996849634..66a80e3eb2 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs @@ -12,7 +12,7 @@ namespace ServiceControl.Persistence.EFCore.Implementation; public class GroupsDataStore(IServiceScopeFactory scopeFactory) : DataStoreBase(scopeFactory), IGroupsDataStore { - public Task> GetUnresolvedGroupsByClassifier(string classifier, string classifierFilter, CancellationToken cancellationToken = default) => + public Task> GetUnresolvedGroupsByClassifier(string classifier, string? classifierFilter, CancellationToken cancellationToken = default) => ExecuteWithDbContext(async (dbContext, token) => { var groups = ByClassifier(dbContext, classifier); @@ -33,16 +33,16 @@ public Task> GetArchivedGroupsByClassifier(string classi ExecuteWithDbContext((dbContext, token) => MostRecent( ByClassifier(dbContext, classifier).AggregateGroups(WithStatus(dbContext, FailedMessageStatus.Archived)), token), cancellationToken); - public Task> GetUnresolvedGroup(string groupId, string status, string modified, CancellationToken cancellationToken = default) => + public Task> GetUnresolvedGroup(string groupId, string? status, string? modified, CancellationToken cancellationToken = default) => ExecuteWithDbContext((dbContext, token) => SingleGroup(dbContext, groupId, FailedMessageStatus.Unresolved, status, modified, token), cancellationToken); - public Task> GetArchivedGroup(string groupId, string status, string modified, CancellationToken cancellationToken = default) => + public Task> GetArchivedGroup(string groupId, string? status, string? modified, CancellationToken cancellationToken = default) => ExecuteWithDbContext((dbContext, token) => SingleGroup(dbContext, groupId, FailedMessageStatus.Archived, status, modified, token), cancellationToken); - public Task>> GetGroupErrors(string groupId, string status, string modified, SortInfo sortInfo, PagingInfo pagingInfo, CancellationToken cancellationToken = default) => + public Task>> GetGroupErrors(string groupId, string? status, string? modified, SortInfo sortInfo, PagingInfo pagingInfo, CancellationToken cancellationToken = default) => ExecuteWithDbContext((dbContext, token) => InGroup(dbContext, groupId, status, modified).ToPagedResult(pagingInfo, sortInfo, token), cancellationToken); - public Task GetGroupErrorsCount(string groupId, string status, string modified, CancellationToken cancellationToken = default) => + public Task GetGroupErrorsCount(string groupId, string? status, string? modified, CancellationToken cancellationToken = default) => ExecuteWithDbContext((dbContext, token) => InGroup(dbContext, groupId, status, modified).ToQueryStatsInfo(token), cancellationToken); public Task EditComment(string groupId, string comment, CancellationToken cancellationToken = default) => @@ -73,7 +73,7 @@ static IQueryable ByClassifier(ServiceControlDbContext .AsNoTracking() .Where(group => group.Type == classifier); - static async Task> SingleGroup(ServiceControlDbContext dbContext, string groupId, FailedMessageStatus baseline, string status, string modified, CancellationToken cancellationToken) + static async Task> SingleGroup(ServiceControlDbContext dbContext, string groupId, FailedMessageStatus baseline, string? status, string? modified, CancellationToken cancellationToken) { var groups = await dbContext.FailedMessageGroups .AsNoTracking() @@ -91,7 +91,7 @@ static IQueryable WithStatus(ServiceControlDbContext dbCont .AsNoTracking() .Where(message => message.Status == status); - static IQueryable InGroup(ServiceControlDbContext dbContext, string groupId, string status, string modified) => + static IQueryable InGroup(ServiceControlDbContext dbContext, string groupId, string? status, string? modified) => dbContext.FailedMessages .AsNoTracking() .Where(message => dbContext.FailedMessageGroups.Any(group => group.GroupId == groupId && group.FailedMessageUniqueId == message.UniqueMessageId)) diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchStore.cs index 9d88d8acb4..0bbb8bb714 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchStore.cs @@ -11,7 +11,7 @@ namespace ServiceControl.Persistence.EFCore.Implementation; public class RetryBatchStore(IServiceScopeFactory scopeFactory, IRetryBatchSqlDialect dialect) : DataStoreBase(scopeFactory), IRetryBatchStore { public Task CreateBatch(string retrySessionId, string requestId, RetryType retryType, - string[] failedMessageRetryIds, string originator, DateTime startTime, DateTime? last = null, + string[] failedMessageRetryIds, string? originator, DateTime startTime, DateTime? last = null, string? batchName = null, string? classifier = null, string? initiatedById = null, string? initiatedByName = null, string? operationId = null, CancellationToken cancellationToken = default) => diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs index 423328625d..1b94be8b5a 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs @@ -51,7 +51,7 @@ public Task GetRetryHistory(CancellationToken cancellationToken = }, cancellationToken); public Task RecordRetryOperationCompleted(string requestId, RetryType retryType, DateTime startTime, DateTime completionTime, - string originator, string classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, int retryHistoryDepth, + string? originator, string? classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, int retryHistoryDepth, CancellationToken cancellationToken = default) => ExecuteWithDbContext(async (dbContext, token) => { @@ -101,7 +101,7 @@ static bool NeedsAcknowledgement(RetryType retryType) => retryType is not RetryType.SingleMessage and not RetryType.MultipleMessages; static async Task RecordUnacknowledged(ServiceControlDbContext dbContext, string requestId, RetryType retryType, - DateTime startTime, DateTime completionTime, string originator, string classifier, bool messageFailed, + DateTime startTime, DateTime completionTime, string? originator, string? classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, CancellationToken cancellationToken) { var unacknowledged = await dbContext.UnacknowledgedRetryOperations diff --git a/src/ServiceControl.Persistence.RavenDB/EndpointDetailsParser.cs b/src/ServiceControl.Persistence.RavenDB/EndpointDetailsParser.cs index 1cda01af30..9cb81640b4 100644 --- a/src/ServiceControl.Persistence.RavenDB/EndpointDetailsParser.cs +++ b/src/ServiceControl.Persistence.RavenDB/EndpointDetailsParser.cs @@ -10,7 +10,7 @@ class EndpointDetailsParser { public static EndpointDetails SendingEndpoint(IReadOnlyDictionary headers) { - var endpointDetails = new EndpointDetails(); + var endpointDetails = new EndpointDetails() { Name = "", Host = "" }; DictionaryExtensions.CheckIfKeyExists(Headers.OriginatingEndpoint, headers, s => endpointDetails.Name = s); DictionaryExtensions.CheckIfKeyExists("NServiceBus.OriginatingMachine", headers, s => endpointDetails.Host = s); @@ -37,7 +37,7 @@ public static EndpointDetails SendingEndpoint(IReadOnlyDictionary headers) { - var endpoint = new EndpointDetails(); + var endpoint = new EndpointDetails() { Name = "", Host = "" }; if (headers.TryGetValue(Headers.HostId, out var hostIdHeader)) { diff --git a/src/ServiceControl.Persistence.Tests.RavenDB/CompositeViews/MessagesViewTests.cs b/src/ServiceControl.Persistence.Tests.RavenDB/CompositeViews/MessagesViewTests.cs index 3a94566755..6d6dca3bd2 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDB/CompositeViews/MessagesViewTests.cs +++ b/src/ServiceControl.Persistence.Tests.RavenDB/CompositeViews/MessagesViewTests.cs @@ -213,6 +213,7 @@ public async Task Correct_status_for_failed_messages(FailedMessageStatus failedM session.Store(new FailedMessage { Id = "1", + UniqueMessageId = Guid.NewGuid().ToString(), ProcessingAttempts = [ new FailedMessage.ProcessingAttempt @@ -255,6 +256,7 @@ public async Task Correct_status_for_repeated_errors() session.Store(new FailedMessage { Id = "1", + UniqueMessageId = Guid.NewGuid().ToString(), ProcessingAttempts = [ new FailedMessage.ProcessingAttempt diff --git a/src/ServiceControl.Persistence.Tests.RavenDB/FailedMessageBuilder.cs b/src/ServiceControl.Persistence.Tests.RavenDB/FailedMessageBuilder.cs index fedf805189..521a6cfa77 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDB/FailedMessageBuilder.cs +++ b/src/ServiceControl.Persistence.Tests.RavenDB/FailedMessageBuilder.cs @@ -15,6 +15,7 @@ public static FailedMessage Minimal(Action customize) var message = new FailedMessage { Id = "1", + UniqueMessageId = Guid.NewGuid().ToString(), ProcessingAttempts = [ new FailedMessage.ProcessingAttempt diff --git a/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs b/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs index 0424b0071b..785c7aacb3 100644 --- a/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs +++ b/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs @@ -4,7 +4,6 @@ namespace ServiceControl.Persistence.Tests; using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using EFCore.DbContexts; @@ -52,7 +51,7 @@ static async Task InsertFailedMessagesDirect(IServiceProvider serviceProvider, F var contentType = attempt.Headers.GetValueOrDefault(Headers.ContentType, "text/plain"); db.FailedMessages.Add(new FailedMessageEntity { - UniqueMessageId = Guid.Parse(failedMessage.UniqueMessageId), + UniqueMessageId = Guid.Parse(failedMessage.UniqueMessageId!), FirstTimeOfFailure = ordered.Min(pa => pa.FailureDetails.TimeOfFailure), LastTimeOfFailure = ordered.Max(pa => pa.FailureDetails.TimeOfFailure), LastAttemptedAt = attempt.AttemptedAt, diff --git a/src/ServiceControl.Persistence.Tests/MessageRedirects/MessageRedirectsDataStoreTests.cs b/src/ServiceControl.Persistence.Tests/MessageRedirects/MessageRedirectsDataStoreTests.cs index cdf18015e1..0926367b9f 100644 --- a/src/ServiceControl.Persistence.Tests/MessageRedirects/MessageRedirectsDataStoreTests.cs +++ b/src/ServiceControl.Persistence.Tests/MessageRedirects/MessageRedirectsDataStoreTests.cs @@ -96,7 +96,7 @@ public async Task Removes_a_redirect() await Add("Sales", "Sales.New"); await Add("Shipping", "Shipping.New"); - await MessageRedirectsDataStore.RemoveRedirect(new MessageRedirect { FromPhysicalAddress = "Sales" }); + await MessageRedirectsDataStore.RemoveRedirect(new MessageRedirect { FromPhysicalAddress = "Sales", ToPhysicalAddress = "Sales.New" }); var redirects = await MessageRedirectsDataStore.GetRedirects(); @@ -112,7 +112,7 @@ public async Task Ignores_removing_a_redirect_that_is_not_there() { await Add("Sales", "Sales.New"); - await MessageRedirectsDataStore.RemoveRedirect(new MessageRedirect { FromPhysicalAddress = "Unknown" }); + await MessageRedirectsDataStore.RemoveRedirect(new MessageRedirect { FromPhysicalAddress = "Unknown", ToPhysicalAddress = "Sales" }); Assert.That(await MessageRedirectsDataStore.GetRedirects(), Has.Count.EqualTo(1)); } diff --git a/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs b/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs index f5f5510f78..62bf5eda6f 100644 --- a/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs +++ b/src/ServiceControl.Persistence.Tests/Recoverability/RetryConfirmationProcessorTests.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.Tests.Recoverability +namespace ServiceControl.Persistence.Tests.Recoverability { using System; using System.Collections.Generic; @@ -23,6 +23,7 @@ await PersistenceTestsContext.InsertFailedMessages( new FailedMessage { Id = MessageId, + UniqueMessageId = Guid.NewGuid().ToString(), Status = FailedMessageStatus.Unresolved } ); diff --git a/src/ServiceControl.Persistence/CustomCheck.cs b/src/ServiceControl.Persistence/CustomCheck.cs index 584598a68f..d16a5a0d01 100644 --- a/src/ServiceControl.Persistence/CustomCheck.cs +++ b/src/ServiceControl.Persistence/CustomCheck.cs @@ -6,12 +6,12 @@ public class CustomCheck { - public string Id { get; set; } - public string CustomCheckId { get; set; } - public string Category { get; set; } + public string? Id { get; set; } + public string? CustomCheckId { get; set; } + public string? Category { get; set; } public Status Status { get; set; } public DateTime ReportedAt { get; set; } - public string FailureReason { get; set; } - public EndpointDetails OriginatingEndpoint { get; set; } + public string? FailureReason { get; set; } + public EndpointDetails? OriginatingEndpoint { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/CustomCheckDetail.cs b/src/ServiceControl.Persistence/CustomCheckDetail.cs index 010a554943..d340b280d7 100644 --- a/src/ServiceControl.Persistence/CustomCheckDetail.cs +++ b/src/ServiceControl.Persistence/CustomCheckDetail.cs @@ -17,13 +17,13 @@ public CustomCheckDetail() ReportedAt = DateTime.UtcNow; } - public EndpointDetails OriginatingEndpoint { get; set; } - public string CustomCheckId { get; set; } + public required EndpointDetails OriginatingEndpoint { get; set; } + public required string CustomCheckId { get; set; } public DateTime ReportedAt { get; set; } - public string Category { get; set; } + public required string Category { get; set; } public bool HasFailed { get; set; } - public string FailureReason { get; set; } + public string? FailureReason { get; set; } - public Guid GetDeterministicId() => DeterministicGuid.MakeId(OriginatingEndpoint.Name, OriginatingEndpoint.HostId.ToString(), CustomCheckId); + public Guid GetDeterministicId() => DeterministicGuid.MakeId(OriginatingEndpoint.Name ?? "", OriginatingEndpoint.HostId.ToString(), CustomCheckId); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/EmailNotifications.cs b/src/ServiceControl.Persistence/EmailNotifications.cs index 761cf5e2ab..c50d0128ee 100644 --- a/src/ServiceControl.Persistence/EmailNotifications.cs +++ b/src/ServiceControl.Persistence/EmailNotifications.cs @@ -4,18 +4,18 @@ public class EmailNotifications { public bool Enabled { get; set; } - public string SmtpServer { get; set; } + public string? SmtpServer { get; set; } public int? SmtpPort { get; set; } - public string AuthenticationAccount { get; set; } + public string? AuthenticationAccount { get; set; } - public string AuthenticationPassword { get; set; } + public string? AuthenticationPassword { get; set; } public bool EnableTLS { get; set; } - public string To { get; set; } + public string? To { get; set; } - public string From { get; set; } + public string? From { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/EndpointDetails.cs b/src/ServiceControl.Persistence/EndpointDetails.cs index c123ab6dba..cc73118c20 100644 --- a/src/ServiceControl.Persistence/EndpointDetails.cs +++ b/src/ServiceControl.Persistence/EndpointDetails.cs @@ -5,12 +5,12 @@ namespace ServiceControl.Operations public class EndpointDetails { - public string Name { get; set; } + public required string Name { get; set; } public Guid HostId { get; set; } - public string Host { get; set; } + public required string Host { get; set; } - public Guid GetDeterministicId() => DeterministicGuid.MakeId(Name, HostId.ToString()); + public Guid GetDeterministicId() => DeterministicGuid.MakeId(Name ?? "", HostId.ToString()); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/EndpointInstanceId.cs b/src/ServiceControl.Persistence/EndpointInstanceId.cs index 36d4448879..388eb1624c 100644 --- a/src/ServiceControl.Persistence/EndpointInstanceId.cs +++ b/src/ServiceControl.Persistence/EndpointInstanceId.cs @@ -15,7 +15,7 @@ public EndpointInstanceId(string logicalName, string hostName, Guid hostGuid) public Guid UniqueId { get; } - public bool Equals(EndpointInstanceId other) + public bool Equals(EndpointInstanceId? other) { if (other is null) { @@ -30,7 +30,7 @@ public bool Equals(EndpointInstanceId other) return string.Equals(LogicalName, other.LogicalName) && string.Equals(HostName, other.HostName) && HostGuid.Equals(other.HostGuid); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return Equals(obj as EndpointInstanceId); } diff --git a/src/ServiceControl.Persistence/EndpointSettings.cs b/src/ServiceControl.Persistence/EndpointSettings.cs index 46f7dab685..90ce7da5e2 100644 --- a/src/ServiceControl.Persistence/EndpointSettings.cs +++ b/src/ServiceControl.Persistence/EndpointSettings.cs @@ -2,6 +2,6 @@ public class EndpointSettings { - public string Name { get; set; } + public required string Name { get; set; } public bool TrackInstances { get; set; } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/EndpointsView.cs b/src/ServiceControl.Persistence/EndpointsView.cs index 7be54e4c01..59f8313ab1 100644 --- a/src/ServiceControl.Persistence/EndpointsView.cs +++ b/src/ServiceControl.Persistence/EndpointsView.cs @@ -5,11 +5,11 @@ namespace ServiceControl.Persistence public class EndpointsView { public Guid Id { get; set; } - public string Name { get; set; } - public string HostDisplayName { get; set; } + public required string Name { get; set; } + public string? HostDisplayName { get; set; } public bool Monitored { get; set; } public bool MonitorHeartbeat { get; set; } - public HeartbeatInformation HeartbeatInformation { get; set; } + public HeartbeatInformation? HeartbeatInformation { get; set; } public bool IsSendingHeartbeats { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/EventLog/EventLogItem.cs b/src/ServiceControl.Persistence/EventLog/EventLogItem.cs index 563416ecd7..2d73d06aa1 100644 --- a/src/ServiceControl.Persistence/EventLog/EventLogItem.cs +++ b/src/ServiceControl.Persistence/EventLog/EventLogItem.cs @@ -9,14 +9,14 @@ /// public class EventLogItem { - public string Description { get; set; } + public string? Description { get; set; } public Severity Severity { get; set; } public DateTime RaisedAt { get; set; } /// /// This could be the Id of a related document, such as the FailedMessage event, which will have more information regarding this alert. /// - public List RelatedTo { get; set; } - public string Category { get; set; } - public string EventType { get; set; } + public List RelatedTo { get; set; } = []; + public required string Category { get; set; } + public required string EventType { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/EventLog/EventLogItemView.cs b/src/ServiceControl.Persistence/EventLog/EventLogItemView.cs index c3646ac8fc..c7a67d1de6 100644 --- a/src/ServiceControl.Persistence/EventLog/EventLogItemView.cs +++ b/src/ServiceControl.Persistence/EventLog/EventLogItemView.cs @@ -11,12 +11,12 @@ public class EventLogItemView /// /// Assigned by whichever persister stored the item, and opaque. /// - public string Id { get; set; } - public string Description { get; set; } + public required string Id { get; set; } + public required string Description { get; set; } public Severity Severity { get; set; } public DateTime RaisedAt { get; set; } - public List RelatedTo { get; set; } - public string Category { get; set; } - public string EventType { get; set; } + public List RelatedTo { get; set; } = []; + public required string Category { get; set; } + public required string EventType { get; set; } } } diff --git a/src/ServiceControl.Persistence/ExceptionDetails.cs b/src/ServiceControl.Persistence/ExceptionDetails.cs index ef5c86ebd1..83bcb56215 100644 --- a/src/ServiceControl.Persistence/ExceptionDetails.cs +++ b/src/ServiceControl.Persistence/ExceptionDetails.cs @@ -2,9 +2,9 @@ namespace ServiceControl.Contracts.Operations { public class ExceptionDetails { - public string ExceptionType { get; set; } - public string Message { get; set; } - public string Source { get; set; } - public string StackTrace { get; set; } + public string? ExceptionType { get; set; } + public string? Message { get; set; } + public string? Source { get; set; } + public string? StackTrace { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs b/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs index 14a15dafa0..94a78a27d3 100644 --- a/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs +++ b/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs @@ -2,7 +2,7 @@ namespace ServiceControl.ExternalIntegrations { public class ExternalIntegrationDispatchRequest { - public string Id { get; set; } - public object DispatchContext; + public string? Id { get; set; } + public required object DispatchContext; } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/FailedErrorImport.cs b/src/ServiceControl.Persistence/FailedErrorImport.cs index 32e58afef4..c7849d9ec4 100644 --- a/src/ServiceControl.Persistence/FailedErrorImport.cs +++ b/src/ServiceControl.Persistence/FailedErrorImport.cs @@ -6,9 +6,9 @@ namespace ServiceControl.Operations public class FailedErrorImport { - public string Id { get; set; } - public FailedTransportMessage Message { get; set; } - public string ExceptionInfo { get; set; } + public required string Id { get; set; } + public FailedTransportMessage? Message { get; set; } + public string? ExceptionInfo { get; set; } public static Guid DeriveKey(IReadOnlyDictionary headers, string nativeMessageId) { diff --git a/src/ServiceControl.Persistence/FailedMessage.cs b/src/ServiceControl.Persistence/FailedMessage.cs index 74c871b2c9..d8a1285fb3 100644 --- a/src/ServiceControl.Persistence/FailedMessage.cs +++ b/src/ServiceControl.Persistence/FailedMessage.cs @@ -12,12 +12,12 @@ public FailedMessage() FailureGroups = []; } - public string Id { get; set; } + public string? Id { get; set; } public List ProcessingAttempts { get; set; } public List FailureGroups { get; set; } - public string UniqueMessageId { get; set; } + public required string UniqueMessageId { get; set; } public FailedMessageStatus Status { get; set; } @@ -31,25 +31,25 @@ public ProcessingAttempt() } public Dictionary MessageMetadata { get; set; } - public FailureDetails FailureDetails { get; set; } + public FailureDetails FailureDetails { get; set; } = new(); public DateTime AttemptedAt { get; set; } - public string MessageId { get; set; } - public string Body { get; set; } + public string? MessageId { get; set; } + public string? Body { get; set; } public Dictionary Headers { get; set; } } public class FailureGroup { - public string Id { get; set; } - public string Title { get; set; } - public string Type { get; set; } + public required string Id { get; set; } + public string? Title { get; set; } + public string? Type { get; set; } } } public class GroupComment { - public string Id { get; set; } - public string Comment { get; set; } + public required string Id { get; set; } + public string? Comment { get; set; } } public enum FailedMessageStatus diff --git a/src/ServiceControl.Persistence/FailedMessageView.cs b/src/ServiceControl.Persistence/FailedMessageView.cs index 8551812fad..557628724e 100644 --- a/src/ServiceControl.Persistence/FailedMessageView.cs +++ b/src/ServiceControl.Persistence/FailedMessageView.cs @@ -6,20 +6,20 @@ public class FailedMessageView { - public string Id { get; set; } - public string MessageType { get; set; } + public required string Id { get; set; } + public string? MessageType { get; set; } public DateTime? TimeSent { get; set; } public bool IsSystemMessage { get; set; } - public ExceptionDetails Exception { get; set; } - public string MessageId { get; set; } + public required ExceptionDetails Exception { get; set; } + public string? MessageId { get; set; } public int NumberOfProcessingAttempts { get; set; } public FailedMessageStatus Status { get; set; } - public EndpointDetails SendingEndpoint { get; set; } - public EndpointDetails ReceivingEndpoint { get; set; } - public string QueueAddress { get; set; } + public EndpointDetails? SendingEndpoint { get; set; } + public EndpointDetails? ReceivingEndpoint { get; set; } + public string? QueueAddress { get; set; } public DateTime TimeOfFailure { get; set; } public DateTime LastModified { get; set; } public bool Edited { get; set; } - public string EditOf { get; set; } + public string? EditOf { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/FailedTransportMessage.cs b/src/ServiceControl.Persistence/FailedTransportMessage.cs index d2604ea448..00c3286c26 100644 --- a/src/ServiceControl.Persistence/FailedTransportMessage.cs +++ b/src/ServiceControl.Persistence/FailedTransportMessage.cs @@ -4,8 +4,8 @@ public class FailedTransportMessage { - public string Id { get; set; } - public Dictionary Headers { get; set; } - public byte[] Body { get; set; } + public required string Id { get; set; } + public required Dictionary Headers { get; set; } + public required byte[] Body { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/FailureDetails.cs b/src/ServiceControl.Persistence/FailureDetails.cs index 81d7be97e7..63275a6d7f 100644 --- a/src/ServiceControl.Persistence/FailureDetails.cs +++ b/src/ServiceControl.Persistence/FailureDetails.cs @@ -9,10 +9,10 @@ public FailureDetails() TimeOfFailure = DateTime.UtcNow; } - public string AddressOfFailingEndpoint { get; set; } + public string? AddressOfFailingEndpoint { get; set; } public DateTime TimeOfFailure { get; set; } - public ExceptionDetails Exception { get; set; } + public ExceptionDetails? Exception { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/FailureGroupMessageView.cs b/src/ServiceControl.Persistence/FailureGroupMessageView.cs index 215621578f..edf908bf40 100644 --- a/src/ServiceControl.Persistence/FailureGroupMessageView.cs +++ b/src/ServiceControl.Persistence/FailureGroupMessageView.cs @@ -5,12 +5,12 @@ namespace ServiceControl.Recoverability public class FailureGroupMessageView : IHaveStatus { - public string Id { get; set; } - public string FailureGroupId { get; set; } - public string FailureGroupName { get; set; } - public string MessageId { get; set; } + public required string Id { get; set; } + public required string FailureGroupId { get; set; } + public required string FailureGroupName { get; set; } + public required string MessageId { get; set; } public DateTime TimeSent { get; set; } - public string MessageType { get; set; } + public required string MessageType { get; set; } public DateTime TimeOfFailure { get; set; } public long LastModified { get; set; } public FailedMessageStatus Status { get; set; } diff --git a/src/ServiceControl.Persistence/FailureGroupView.cs b/src/ServiceControl.Persistence/FailureGroupView.cs index c8ab0ad9ab..8fa88024da 100644 --- a/src/ServiceControl.Persistence/FailureGroupView.cs +++ b/src/ServiceControl.Persistence/FailureGroupView.cs @@ -4,11 +4,11 @@ namespace ServiceControl.Recoverability public class FailureGroupView { - public string Id { get; set; } - public string Title { get; set; } - public string Type { get; set; } + public required string Id { get; set; } + public required string Title { get; set; } + public required string Type { get; set; } public int Count { get; set; } - public string Comment { get; set; } + public string? Comment { get; set; } public DateTime First { get; set; } public DateTime Last { get; set; } } diff --git a/src/ServiceControl.Persistence/ForwardingRetryBatch.cs b/src/ServiceControl.Persistence/ForwardingRetryBatch.cs index 7188a65def..01253b91e0 100644 --- a/src/ServiceControl.Persistence/ForwardingRetryBatch.cs +++ b/src/ServiceControl.Persistence/ForwardingRetryBatch.cs @@ -3,5 +3,5 @@ namespace ServiceControl.Persistence /// /// The batch currently being forwarded. /// - public record ForwardingRetryBatch(string RequestId, RetryType RetryType, string Originator, string Classifier); + public record ForwardingRetryBatch(string RequestId, RetryType RetryType, string? Originator, string? Classifier); } diff --git a/src/ServiceControl.Persistence/GroupOperation.cs b/src/ServiceControl.Persistence/GroupOperation.cs index 5e536f7c00..64567795c1 100644 --- a/src/ServiceControl.Persistence/GroupOperation.cs +++ b/src/ServiceControl.Persistence/GroupOperation.cs @@ -2,15 +2,15 @@ public class GroupOperation { - public string Id { get; set; } - public string Title { get; set; } - public string Type { get; set; } + public string? Id { get; set; } + public string? Title { get; set; } + public string? Type { get; set; } public int Count { get; set; } public int? OperationMessagesCompletedCount { get; set; } - public string Comment { get; set; } + public string? Comment { get; set; } public DateTime? First { get; set; } public DateTime? Last { get; set; } - public string OperationStatus { get; set; } + public string? OperationStatus { get; set; } public bool? OperationFailed { get; set; } public double OperationProgress { get; set; } public int? OperationRemainingCount { get; set; } diff --git a/src/ServiceControl.Persistence/History/HistoricRetryOperation.cs b/src/ServiceControl.Persistence/History/HistoricRetryOperation.cs index 80c6bf49bf..fc6b9d6310 100644 --- a/src/ServiceControl.Persistence/History/HistoricRetryOperation.cs +++ b/src/ServiceControl.Persistence/History/HistoricRetryOperation.cs @@ -5,11 +5,11 @@ public class HistoricRetryOperation { - public string RequestId { get; set; } + public required string RequestId { get; set; } public RetryType RetryType { get; set; } public DateTime StartTime { get; set; } public DateTime CompletionTime { get; set; } - public string Originator { get; set; } + public string? Originator { get; set; } public bool Failed { get; set; } public int NumberOfMessagesProcessed { get; set; } } diff --git a/src/ServiceControl.Persistence/History/UnacknowledgedRetryOperation.cs b/src/ServiceControl.Persistence/History/UnacknowledgedRetryOperation.cs index 7a0d684bbe..3e1f3b8730 100644 --- a/src/ServiceControl.Persistence/History/UnacknowledgedRetryOperation.cs +++ b/src/ServiceControl.Persistence/History/UnacknowledgedRetryOperation.cs @@ -5,13 +5,13 @@ public class UnacknowledgedRetryOperation { - public string RequestId { get; set; } + public required string RequestId { get; set; } public RetryType RetryType { get; set; } public DateTime StartTime { get; set; } public DateTime CompletionTime { get; set; } public DateTime Last { get; set; } - public string Originator { get; set; } - public string Classifier { get; set; } + public string? Originator { get; set; } + public string? Classifier { get; set; } public bool Failed { get; set; } public int NumberOfMessagesProcessed { get; set; } } diff --git a/src/ServiceControl.Persistence/IBodyStorage.cs b/src/ServiceControl.Persistence/IBodyStorage.cs index 483e347206..7d7715437f 100644 --- a/src/ServiceControl.Persistence/IBodyStorage.cs +++ b/src/ServiceControl.Persistence/IBodyStorage.cs @@ -20,7 +20,7 @@ public enum MessageBodyState public sealed class MessageBodyResult { - MessageBodyResult(MessageBodyState state, MessageBodyStreamContent content = null) + MessageBodyResult(MessageBodyState state, MessageBodyStreamContent? content = null) { State = state; ContentValue = content; @@ -28,9 +28,9 @@ public sealed class MessageBodyResult public MessageBodyState State { get; } - public MessageBodyStreamContent Content => State == MessageBodyState.Available + public MessageBodyStreamContent Content => State == MessageBodyState.Available && ContentValue is not null ? ContentValue - : throw new InvalidOperationException($"Body content is not available when the state is {State}."); + : throw new InvalidOperationException($"Body content is not available when the state is {State} or content is null."); public static MessageBodyResult NotFound() => new(MessageBodyState.NotFound); @@ -44,7 +44,7 @@ public static MessageBodyResult Available(MessageBodyStreamContent content) return new MessageBodyResult(MessageBodyState.Available, content); } - MessageBodyStreamContent ContentValue { get; } + MessageBodyStreamContent? ContentValue { get; } } public sealed record MessageBodyStreamContent(Stream Stream, string ContentType, int BodySize, string Etag); diff --git a/src/ServiceControl.Persistence/ICustomChecksDataStore.cs b/src/ServiceControl.Persistence/ICustomChecksDataStore.cs index 4102c04506..5b48c625ee 100644 --- a/src/ServiceControl.Persistence/ICustomChecksDataStore.cs +++ b/src/ServiceControl.Persistence/ICustomChecksDataStore.cs @@ -11,7 +11,7 @@ public interface ICustomChecksDataStore { Task UpdateCustomCheckStatus(CustomCheckDetail detail, CancellationToken cancellationToken = default); - Task>> GetStats(PagingInfo paging, string status = null, CancellationToken cancellationToken = default); + Task>> GetStats(PagingInfo paging, string? status = null, CancellationToken cancellationToken = default); Task DeleteCustomCheck(Guid id, CancellationToken cancellationToken = default); Task GetNumberOfFailedChecks(CancellationToken cancellationToken = default); } diff --git a/src/ServiceControl.Persistence/IEventLogDataStore.cs b/src/ServiceControl.Persistence/IEventLogDataStore.cs index cfbad88a64..b130161cfb 100644 --- a/src/ServiceControl.Persistence/IEventLogDataStore.cs +++ b/src/ServiceControl.Persistence/IEventLogDataStore.cs @@ -41,6 +41,6 @@ public interface IEventLogDataStore /// one is added, since nothing else tells a client its cached page is now wrong. /// Task>> GetEventLogItems( - PagingInfo pagingInfo, string knownVersion = null, CancellationToken cancellationToken = default); + PagingInfo pagingInfo, string? knownVersion = null, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/IGroupsDataStore.cs b/src/ServiceControl.Persistence/IGroupsDataStore.cs index 0c7493680e..5f389f10fe 100644 --- a/src/ServiceControl.Persistence/IGroupsDataStore.cs +++ b/src/ServiceControl.Persistence/IGroupsDataStore.cs @@ -9,13 +9,13 @@ namespace ServiceControl.Persistence public interface IGroupsDataStore { - Task> GetUnresolvedGroupsByClassifier(string classifier, string classifierFilter, CancellationToken cancellationToken = default); + Task> GetUnresolvedGroupsByClassifier(string classifier, string? classifierFilter, CancellationToken cancellationToken = default); Task> GetArchivedGroupsByClassifier(string classifier, CancellationToken cancellationToken = default); - Task> GetUnresolvedGroup(string groupId, string status, string modified, CancellationToken cancellationToken = default); - Task> GetArchivedGroup(string groupId, string status, string modified, CancellationToken cancellationToken = default); - Task>> GetGroupErrors(string groupId, string status, string modified, SortInfo sortInfo, PagingInfo pagingInfo, CancellationToken cancellationToken = default); - Task GetGroupErrorsCount(string groupId, string status, string modified, CancellationToken cancellationToken = default); + Task> GetUnresolvedGroup(string groupId, string? status, string? modified, CancellationToken cancellationToken = default); + Task> GetArchivedGroup(string groupId, string? status, string? modified, CancellationToken cancellationToken = default); + Task>> GetGroupErrors(string groupId, string? status, string? modified, SortInfo sortInfo, PagingInfo pagingInfo, CancellationToken cancellationToken = default); + Task GetGroupErrorsCount(string groupId, string? status, string? modified, CancellationToken cancellationToken = default); Task EditComment(string groupId, string comment, CancellationToken cancellationToken = default); Task DeleteComment(string groupId, CancellationToken cancellationToken = default); diff --git a/src/ServiceControl.Persistence/IMessagesViewDataStore.cs b/src/ServiceControl.Persistence/IMessagesViewDataStore.cs index ad7ae30200..66b45c9302 100644 --- a/src/ServiceControl.Persistence/IMessagesViewDataStore.cs +++ b/src/ServiceControl.Persistence/IMessagesViewDataStore.cs @@ -9,10 +9,10 @@ namespace ServiceControl.Persistence public interface IMessagesViewDataStore { - Task>> GetAllMessages(PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange timeSentRange = null, CancellationToken cancellationToken = default); - Task>> GetAllMessagesForEndpoint(string endpointName, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange timeSentRange = null, CancellationToken cancellationToken = default); + Task>> GetAllMessages(PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange? timeSentRange = null, CancellationToken cancellationToken = default); + Task>> GetAllMessagesForEndpoint(string endpointName, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange? timeSentRange = null, CancellationToken cancellationToken = default); Task>> GetAllMessagesByConversation(string conversationId, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, CancellationToken cancellationToken = default); - Task>> GetAllMessagesForSearch(string searchTerms, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange timeSentRange = null, CancellationToken cancellationToken = default); - Task>> SearchEndpointMessages(string endpointName, string searchKeyword, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange timeSentRange = null, CancellationToken cancellationToken = default); + Task>> GetAllMessagesForSearch(string searchTerms, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange? timeSentRange = null, CancellationToken cancellationToken = default); + Task>> SearchEndpointMessages(string endpointName, string searchKeyword, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange? timeSentRange = null, CancellationToken cancellationToken = default); } } diff --git a/src/ServiceControl.Persistence/IRetryBatchStore.cs b/src/ServiceControl.Persistence/IRetryBatchStore.cs index 1d1372e882..3f037a3327 100644 --- a/src/ServiceControl.Persistence/IRetryBatchStore.cs +++ b/src/ServiceControl.Persistence/IRetryBatchStore.cs @@ -10,9 +10,9 @@ namespace ServiceControl.Persistence public interface IRetryBatchStore { Task CreateBatch(string retrySessionId, string requestId, RetryType retryType, - string[] failedMessageRetryIds, string originator, DateTime startTime, DateTime? last = null, - string batchName = null, string classifier = null, - string initiatedById = null, string initiatedByName = null, string operationId = null, + string[] failedMessageRetryIds, string? originator, DateTime startTime, DateTime? last = null, + string? batchName = null, string? classifier = null, + string? initiatedById = null, string? initiatedByName = null, string? operationId = null, CancellationToken cancellationToken = default); Task AssignMessagesToBatch(string batchId, string[] messageIds, CancellationToken cancellationToken = default); @@ -22,7 +22,7 @@ Task CreateBatch(string retrySessionId, string requestId, RetryType retr Task>> GetOrphanedBatches(string retrySessionId, CancellationToken cancellationToken = default); Task> GetAvailableBatchGroups(CancellationToken cancellationToken = default); - Task GetCurrentForwardingBatch(CancellationToken cancellationToken = default); + Task GetCurrentForwardingBatch(CancellationToken cancellationToken = default); Task ForEachUnresolvedMessage(Func callback, CancellationToken cancellationToken = default); Task ForEachUnresolvedMessageForEndpoint(string endpoint, Func callback, CancellationToken cancellationToken = default); diff --git a/src/ServiceControl.Persistence/IRetryHistoryDataStore.cs b/src/ServiceControl.Persistence/IRetryHistoryDataStore.cs index a16b7f4d45..5944bc65c5 100644 --- a/src/ServiceControl.Persistence/IRetryHistoryDataStore.cs +++ b/src/ServiceControl.Persistence/IRetryHistoryDataStore.cs @@ -9,7 +9,7 @@ public interface IRetryHistoryDataStore { Task GetRetryHistory(CancellationToken cancellationToken = default); Task RecordRetryOperationCompleted(string requestId, RetryType retryType, DateTime startTime, DateTime completionTime, - string originator, string classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, int retryHistoryDepth, + string? originator, string? classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, int retryHistoryDepth, CancellationToken cancellationToken = default); Task AcknowledgeRetryGroup(string groupId, CancellationToken cancellationToken = default); } diff --git a/src/ServiceControl.Persistence/Infrastructure/DateTimeRange.cs b/src/ServiceControl.Persistence/Infrastructure/DateTimeRange.cs index 562df20c90..a8bb1f8b15 100644 --- a/src/ServiceControl.Persistence/Infrastructure/DateTimeRange.cs +++ b/src/ServiceControl.Persistence/Infrastructure/DateTimeRange.cs @@ -8,7 +8,7 @@ public class DateTimeRange public DateTime? From { get; } public DateTime? To { get; } - public DateTimeRange(string from = null, string to = null) + public DateTimeRange(string? from = null, string? to = null) { if (from != null) { diff --git a/src/ServiceControl.Persistence/Infrastructure/DeterministicGuid.cs b/src/ServiceControl.Persistence/Infrastructure/DeterministicGuid.cs index 0b0b444677..a62131f219 100644 --- a/src/ServiceControl.Persistence/Infrastructure/DeterministicGuid.cs +++ b/src/ServiceControl.Persistence/Infrastructure/DeterministicGuid.cs @@ -6,20 +6,11 @@ public static class DeterministicGuid { - public static Guid MakeId(string data) - { - return DeterministicGuidBuilder(data); - } + public static Guid MakeId(string data) => DeterministicGuidBuilder(data); - public static Guid MakeId(string data1, string data2) - { - return DeterministicGuidBuilder($"{data1}{data2}"); - } + public static Guid MakeId(string data1, string data2) => DeterministicGuidBuilder($"{data1}{data2}"); - public static Guid MakeId(string data1, string data2, string data3) - { - return DeterministicGuidBuilder($"{data1}{data2}{data3}"); - } + public static Guid MakeId(string data1, string data2, string data3) => DeterministicGuidBuilder($"{data1}{data2}{data3}"); static Guid DeterministicGuidBuilder(string input) { diff --git a/src/ServiceControl.Persistence/Infrastructure/QueryResult.cs b/src/ServiceControl.Persistence/Infrastructure/QueryResult.cs index 87e33fe962..be3e6b8377 100644 --- a/src/ServiceControl.Persistence/Infrastructure/QueryResult.cs +++ b/src/ServiceControl.Persistence/Infrastructure/QueryResult.cs @@ -2,12 +2,12 @@ namespace ServiceControl.Persistence.Infrastructure { using System.Threading.Tasks; - public class QueryResult(TOut results, QueryStatsInfo queryStatsInfo) + public class QueryResult(TOut? results, QueryStatsInfo queryStatsInfo) where TOut : class { - public TOut Results { get; } = results; + public TOut? Results { get; } = results; - public string InstanceId { get; set; } + public string? InstanceId { get; set; } public QueryStatsInfo QueryStats { get; } = queryStatsInfo; diff --git a/src/ServiceControl.Persistence/Infrastructure/SortInfo.cs b/src/ServiceControl.Persistence/Infrastructure/SortInfo.cs index e50a1a407d..c06f974ea2 100644 --- a/src/ServiceControl.Persistence/Infrastructure/SortInfo.cs +++ b/src/ServiceControl.Persistence/Infrastructure/SortInfo.cs @@ -5,7 +5,7 @@ using System.Diagnostics; [DebuggerDisplay("{Sort} {Direction}")] - public class SortInfo(string sort = null, string direction = null) + public class SortInfo(string? sort = null, string? direction = null) { public string Direction { get; } = string.IsNullOrWhiteSpace(direction) ? "desc" : direction; public string Sort { get; } = string.IsNullOrWhiteSpace(sort) ? "time_sent" : sort; diff --git a/src/ServiceControl.Persistence/Infrastructure/TransportMessageExtensions.cs b/src/ServiceControl.Persistence/Infrastructure/TransportMessageExtensions.cs index 4aaef5131e..31f934cd46 100644 --- a/src/ServiceControl.Persistence/Infrastructure/TransportMessageExtensions.cs +++ b/src/ServiceControl.Persistence/Infrastructure/TransportMessageExtensions.cs @@ -7,7 +7,7 @@ public static class HeaderExtensions { - public static string ProcessingEndpointName(this IReadOnlyDictionary headers) + public static string? ProcessingEndpointName(this IReadOnlyDictionary headers) { if (headers.TryGetValue(Headers.ProcessingEndpoint, out var endpoint)) { @@ -40,7 +40,7 @@ public static string UniqueId(this IReadOnlyDictionary headers) { return headers.TryGetValue("ServiceControl.Retry.UniqueMessageId", out var existingUniqueMessageId) ? existingUniqueMessageId - : DeterministicGuid.MakeId(headers.MessageId(), headers.ProcessingEndpointName()).ToString(); + : DeterministicGuid.MakeId(headers.MessageId() ?? "", headers.ProcessingEndpointName() ?? "").ToString(); } public static string ProcessingId(this IReadOnlyDictionary headers) @@ -58,13 +58,13 @@ public static string ProcessingId(this IReadOnlyDictionary heade } // NOTE: Duplicated from TransportMessage - public static string MessageId(this IReadOnlyDictionary headers) + public static string? MessageId(this IReadOnlyDictionary headers) { return headers.TryGetValue(Headers.MessageId, out var str) ? str : default; } // NOTE: Duplicated from TransportMessage - public static string CorrelationId(this IReadOnlyDictionary headers) + public static string? CorrelationId(this IReadOnlyDictionary headers) { return headers.TryGetValue(Headers.CorrelationId, out var correlationId) ? correlationId : null; } @@ -102,17 +102,17 @@ public static bool IsBinary(this IReadOnlyDictionary headers) return true; } - static string ReplyToAddress(this IReadOnlyDictionary headers) => headers.GetValueOrDefault(Headers.ReplyToAddress); + static string? ReplyToAddress(this IReadOnlyDictionary headers) => headers.GetValueOrDefault(Headers.ReplyToAddress); - static string ProcessingStarted(this IReadOnlyDictionary headers) => headers.GetValueOrDefault(Headers.ProcessingStarted); + static string? ProcessingStarted(this IReadOnlyDictionary headers) => headers.GetValueOrDefault(Headers.ProcessingStarted); - static string ExtractQueue(string address) + static string? ExtractQueue(string? address) { var atIndex = address?.IndexOf("@", StringComparison.InvariantCulture); - if (atIndex.HasValue && atIndex.Value > -1) + if (atIndex is > -1) { - return address.Substring(0, atIndex.Value); + return address![..atIndex.Value]; } return address; diff --git a/src/ServiceControl.Persistence/KnownEndpoint.cs b/src/ServiceControl.Persistence/KnownEndpoint.cs index ac024a5407..35aeb4a7fb 100644 --- a/src/ServiceControl.Persistence/KnownEndpoint.cs +++ b/src/ServiceControl.Persistence/KnownEndpoint.cs @@ -4,8 +4,8 @@ public class KnownEndpoint { - public string HostDisplayName { get; set; } + public string? HostDisplayName { get; set; } public bool Monitored { get; set; } - public EndpointDetails EndpointDetails { get; set; } + public required EndpointDetails EndpointDetails { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/KnownEndpointsView.cs b/src/ServiceControl.Persistence/KnownEndpointsView.cs index e873e72c75..9a2323ca71 100644 --- a/src/ServiceControl.Persistence/KnownEndpointsView.cs +++ b/src/ServiceControl.Persistence/KnownEndpointsView.cs @@ -6,7 +6,7 @@ public class KnownEndpointsView { public Guid Id { get; set; } - public EndpointDetails EndpointDetails { get; set; } - public string HostDisplayName { get; set; } + public required EndpointDetails EndpointDetails { get; set; } + public string? HostDisplayName { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/MessageRedirects/MessageRedirect.cs b/src/ServiceControl.Persistence/MessageRedirects/MessageRedirect.cs index f5a38f5c01..25c2b01454 100644 --- a/src/ServiceControl.Persistence/MessageRedirects/MessageRedirect.cs +++ b/src/ServiceControl.Persistence/MessageRedirects/MessageRedirect.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Persistence.MessageRedirects +namespace ServiceControl.Persistence.MessageRedirects { using System; using System.Collections.Concurrent; @@ -8,8 +8,8 @@ public class MessageRedirect { public Guid MessageRedirectId => idCache.GetOrAdd(FromPhysicalAddress, DeterministicGuid.MakeId); - public string FromPhysicalAddress { get; set; } - public string ToPhysicalAddress { get; set; } + public required string FromPhysicalAddress { get; set; } + public required string ToPhysicalAddress { get; set; } public DateTime LastModified { get; set; } static ConcurrentDictionary idCache = new ConcurrentDictionary(); } diff --git a/src/ServiceControl.Persistence/MessageRedirects/MessageRedirectExtensions.cs b/src/ServiceControl.Persistence/MessageRedirects/MessageRedirectExtensions.cs index 4e955a9887..3937b12c02 100644 --- a/src/ServiceControl.Persistence/MessageRedirects/MessageRedirectExtensions.cs +++ b/src/ServiceControl.Persistence/MessageRedirects/MessageRedirectExtensions.cs @@ -6,10 +6,10 @@ namespace ServiceControl.Persistence.MessageRedirects public static class MessageRedirectExtensions { - public static MessageRedirect FindByAddress(this IEnumerable redirects, string fromPhysicalAddress) => + public static MessageRedirect? FindByAddress(this IEnumerable redirects, string fromPhysicalAddress) => redirects.SingleOrDefault(redirect => redirect.FromPhysicalAddress == fromPhysicalAddress); - public static MessageRedirect FindById(this IEnumerable redirects, Guid messageRedirectId) => + public static MessageRedirect? FindById(this IEnumerable redirects, Guid messageRedirectId) => redirects.SingleOrDefault(redirect => redirect.MessageRedirectId == messageRedirectId); } } diff --git a/src/ServiceControl.Persistence/MessagesView.cs b/src/ServiceControl.Persistence/MessagesView.cs index f5f662edeb..25bced6e7a 100644 --- a/src/ServiceControl.Persistence/MessagesView.cs +++ b/src/ServiceControl.Persistence/MessagesView.cs @@ -9,25 +9,25 @@ namespace ServiceControl.CompositeViews.Messages public class MessagesView { - public string Id { get; set; } - public string MessageId { get; set; } - public string MessageType { get; set; } - public EndpointDetails SendingEndpoint { get; set; } - public EndpointDetails ReceivingEndpoint { get; set; } + public string? Id { get; set; } + public string? MessageId { get; set; } + public string? MessageType { get; set; } + public EndpointDetails? SendingEndpoint { get; set; } + public EndpointDetails? ReceivingEndpoint { get; set; } public DateTime? TimeSent { get; set; } public DateTime ProcessedAt { get; set; } public TimeSpan CriticalTime { get; set; } public TimeSpan ProcessingTime { get; set; } public TimeSpan DeliveryTime { get; set; } public bool IsSystemMessage { get; set; } - public string ConversationId { get; set; } - public IEnumerable> Headers { get; set; } + public string? ConversationId { get; set; } + public IEnumerable> Headers { get; set; } = []; public MessageStatus Status { get; set; } public MessageIntent MessageIntent { get; set; } - public string BodyUrl { get; set; } + public string? BodyUrl { get; set; } public int BodySize { get; set; } - public List InvokedSagas { get; set; } - public SagaInfo OriginatesFromSaga { get; set; } - public string InstanceId { get; set; } + public List? InvokedSagas { get; set; } + public SagaInfo? OriginatesFromSaga { get; set; } + public string? InstanceId { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/PersistenceManifest.cs b/src/ServiceControl.Persistence/PersistenceManifest.cs index 9279f6cdc6..1c1c6dff73 100644 --- a/src/ServiceControl.Persistence/PersistenceManifest.cs +++ b/src/ServiceControl.Persistence/PersistenceManifest.cs @@ -10,17 +10,17 @@ public class PersistenceManifest { - public string Location { get; set; } + public string? Location { get; set; } - public string Name { get; set; } + public required string Name { get; set; } - public string DisplayName { get; set; } + public required string DisplayName { get; set; } - public string Description { get; set; } + public required string Description { get; set; } - public string AssemblyName { get; set; } + public required string AssemblyName { get; set; } - public string TypeName { get; set; } + public required string TypeName { get; set; } public bool IsSupported { get; set; } = true; @@ -61,10 +61,7 @@ static PersistenceManifestLibrary() { foreach (var manifestFile in Directory.EnumerateFiles(assemblyDirectory, "persistence.manifest", SearchOption.AllDirectories)) { - var manifest = JsonSerializer.Deserialize(File.ReadAllText(manifestFile)); - manifest.Location = Path.GetDirectoryName(manifestFile); - - PersistenceManifests.Add(manifest); + PersistenceManifests.Add(DeserializeManifest(manifestFile)); } } catch (Exception ex) @@ -76,10 +73,7 @@ static PersistenceManifestLibrary() { foreach (var manifestFile in DevelopmentPersistenceLocations.ManifestFiles) { - var manifest = JsonSerializer.Deserialize(File.ReadAllText(manifestFile)); - manifest.Location = Path.GetDirectoryName(manifestFile); - - PersistenceManifests.Add(manifest); + PersistenceManifests.Add(DeserializeManifest(manifestFile)); } } catch (Exception ex) @@ -90,13 +84,23 @@ static PersistenceManifestLibrary() PersistenceManifests.ForEach(m => logger.LogInformation("Found persistence manifest for {ManifestDisplayName}", m.DisplayName)); } + static PersistenceManifest DeserializeManifest(string manifestFile) + { + var manifest = JsonSerializer.Deserialize(File.ReadAllText(manifestFile)) + ?? throw new InvalidDataException($"The persistence manifest '{manifestFile}' is empty or invalid."); + manifest.Location = Path.GetDirectoryName(manifestFile) + ?? throw new InvalidDataException($"The persistence manifest '{manifestFile}' has no containing directory."); + return manifest; + } + static string GetAssemblyDirectory() { var assemblyLocation = typeof(PersistenceManifestLibrary).Assembly.Location; - return Path.GetDirectoryName(assemblyLocation); + return Path.GetDirectoryName(assemblyLocation) + ?? throw new InvalidOperationException("The persistence assembly has no containing directory."); } - public static PersistenceManifest Find(string persistenceType) + public static PersistenceManifest? Find(string persistenceType) { if (persistenceType == null) { diff --git a/src/ServiceControl.Persistence/PersistenceSettings.cs b/src/ServiceControl.Persistence/PersistenceSettings.cs index 70205f9834..c544578a9e 100644 --- a/src/ServiceControl.Persistence/PersistenceSettings.cs +++ b/src/ServiceControl.Persistence/PersistenceSettings.cs @@ -9,7 +9,7 @@ public abstract class PersistenceSettings { public bool MaintenanceMode { get; set; } //HINT: This needs to be here so that ServerControl instance can add an instance specific metadata to tweak the DatabasePath value - public string DatabasePath { get; set; } + public string? DatabasePath { get; set; } public bool EnableFullTextSearchOnBodies { get; set; } = true; diff --git a/src/ServiceControl.Persistence/ProcessedMessage.cs b/src/ServiceControl.Persistence/ProcessedMessage.cs index 050e5ca6c7..63e0557cb5 100644 --- a/src/ServiceControl.Persistence/ProcessedMessage.cs +++ b/src/ServiceControl.Persistence/ProcessedMessage.cs @@ -24,9 +24,9 @@ public ProcessedMessage(Dictionary headers, Dictionary MessageMetadata { get; set; } diff --git a/src/ServiceControl.Persistence/QueueAddress.cs b/src/ServiceControl.Persistence/QueueAddress.cs index 3f6f002157..9cca593a6e 100644 --- a/src/ServiceControl.Persistence/QueueAddress.cs +++ b/src/ServiceControl.Persistence/QueueAddress.cs @@ -2,7 +2,7 @@ { public class QueueAddress { - public string PhysicalAddress { get; set; } + public string? PhysicalAddress { get; set; } public int FailedMessageCount { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/Recoverability/Archiving/IArchiveMessages.cs b/src/ServiceControl.Persistence/Recoverability/Archiving/IArchiveMessages.cs index 5f60b0c3d5..5edc6fc78f 100644 --- a/src/ServiceControl.Persistence/Recoverability/Archiving/IArchiveMessages.cs +++ b/src/ServiceControl.Persistence/Recoverability/Archiving/IArchiveMessages.cs @@ -11,8 +11,8 @@ /// public interface IArchiveMessages { - Task ArchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string operationId = null, CancellationToken cancellationToken = default); - Task UnarchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string operationId = null, CancellationToken cancellationToken = default); + Task ArchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string? operationId = null, CancellationToken cancellationToken = default); + Task UnarchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string? operationId = null, CancellationToken cancellationToken = default); bool IsOperationInProgressFor(string groupId, ArchiveType archiveType); diff --git a/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryArchive.cs b/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryArchive.cs index 1a77aaa457..2453e6e6c6 100644 --- a/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryArchive.cs +++ b/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryArchive.cs @@ -22,7 +22,7 @@ public InMemoryArchive(string requestId, ArchiveType archiveType, IDomainEvents public DateTime? Last { get; set; } public DateTime Started { get; set; } public ArchiveState ArchiveState { get; set; } - public string GroupName { get; set; } + public string? GroupName { get; set; } public string RequestId { get; set; } public ArchiveType ArchiveType { get; set; } diff --git a/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryUnarchive.cs b/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryUnarchive.cs index 48b75179ed..0816fd1856 100644 --- a/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryUnarchive.cs +++ b/src/ServiceControl.Persistence/Recoverability/Archiving/InMemoryUnarchive.cs @@ -22,7 +22,7 @@ public InMemoryUnarchive(string requestId, ArchiveType archiveType, IDomainEvent public DateTime? Last { get; set; } public DateTime Started { get; set; } public ArchiveState ArchiveState { get; set; } - public string GroupName { get; set; } + public string? GroupName { get; set; } public string RequestId { get; set; } public ArchiveType ArchiveType { get; set; } diff --git a/src/ServiceControl.Persistence/Recoverability/Archiving/OperationsManager.cs b/src/ServiceControl.Persistence/Recoverability/Archiving/OperationsManager.cs index 260b793a9b..a954011c8b 100644 --- a/src/ServiceControl.Persistence/Recoverability/Archiving/OperationsManager.cs +++ b/src/ServiceControl.Persistence/Recoverability/Archiving/OperationsManager.cs @@ -6,14 +6,16 @@ public class OperationsManager { public bool IsOperationInProgressFor(string requestId, ArchiveType archiveType) { - var isUnarchiveOpration = UnarchiveOperations.TryGetValue(InMemoryUnarchive.MakeId(requestId, archiveType), + var isUnarchiveOperation = UnarchiveOperations.TryGetValue(InMemoryUnarchive.MakeId(requestId, archiveType), out var unarchiveSummary); - if (!ArchiveOperations.TryGetValue(InMemoryArchive.MakeId(requestId, archiveType), out var archiveSummary) && !isUnarchiveOpration) + if (!ArchiveOperations.TryGetValue(InMemoryArchive.MakeId(requestId, archiveType), out var archiveSummary) && !isUnarchiveOperation) { return false; } - return archiveSummary?.ArchiveState != ArchiveState.ArchiveCompleted && isUnarchiveOpration && unarchiveSummary.ArchiveState != ArchiveState.ArchiveCompleted; + return archiveSummary?.ArchiveState != ArchiveState.ArchiveCompleted + && isUnarchiveOperation + && unarchiveSummary?.ArchiveState != ArchiveState.ArchiveCompleted; } public Dictionary UnarchiveOperations { get; } = []; diff --git a/src/ServiceControl.Persistence/Recoverability/ClassifiableMessageDetails.cs b/src/ServiceControl.Persistence/Recoverability/ClassifiableMessageDetails.cs index b15ea5e46a..3e8e76e177 100644 --- a/src/ServiceControl.Persistence/Recoverability/ClassifiableMessageDetails.cs +++ b/src/ServiceControl.Persistence/Recoverability/ClassifiableMessageDetails.cs @@ -8,7 +8,7 @@ namespace ServiceControl.Recoverability public struct ClassifiableMessageDetails { public ProcessingAttempt ProcessingAttempt { get; } - public FailureDetails Details { get; } + public FailureDetails? Details { get; } public string MessageType { get; } public ClassifiableMessageDetails(FailedMessage message) diff --git a/src/ServiceControl.Persistence/RetryBatch.cs b/src/ServiceControl.Persistence/RetryBatch.cs index 96fe834244..872feeddf5 100644 --- a/src/ServiceControl.Persistence/RetryBatch.cs +++ b/src/ServiceControl.Persistence/RetryBatch.cs @@ -4,14 +4,14 @@ namespace ServiceControl.Persistence public class RetryBatch { - public string Id { get; init; } - public string Context { get; init; } - public string StagingId { get; init; } - public string Originator { get; init; } - public string Classifier { get; init; } + public required string Id { get; init; } + public string? Context { get; init; } + public string? StagingId { get; init; } + public string? Originator { get; init; } + public string? Classifier { get; init; } public DateTime StartTime { get; init; } public DateTime? Last { get; init; } - public string RequestId { get; init; } + public string? RequestId { get; init; } public int InitialBatchSize { get; init; } public RetryType RetryType { get; init; } public RetryBatchStatus Status { get; init; } @@ -29,12 +29,12 @@ public class RetryBatch /// correlated to the API's operation entry by . Null only for legacy /// in-flight commands sent without the headers. /// - public string InitiatedById { get; init; } + public string? InitiatedById { get; init; } /// - public string InitiatedByName { get; init; } + public string? InitiatedByName { get; init; } /// - public string OperationId { get; init; } + public string? OperationId { get; init; } } } diff --git a/src/ServiceControl.Persistence/RetryBatchGroup.cs b/src/ServiceControl.Persistence/RetryBatchGroup.cs index 01dee5b1e3..d8238aae59 100644 --- a/src/ServiceControl.Persistence/RetryBatchGroup.cs +++ b/src/ServiceControl.Persistence/RetryBatchGroup.cs @@ -4,7 +4,7 @@ namespace ServiceControl.Persistence public class RetryBatchGroup { - public string RequestId { get; set; } + public required string RequestId { get; set; } public RetryType RetryType { get; set; } @@ -14,9 +14,9 @@ public class RetryBatchGroup public int InitialBatchSize { get; set; } - public string Originator { get; set; } + public string? Originator { get; set; } - public string Classifier { get; set; } + public string? Classifier { get; set; } public DateTime StartTime { get; set; } diff --git a/src/ServiceControl.Persistence/ServiceControl.Persistence.csproj b/src/ServiceControl.Persistence/ServiceControl.Persistence.csproj index f32e653492..f4f4299197 100644 --- a/src/ServiceControl.Persistence/ServiceControl.Persistence.csproj +++ b/src/ServiceControl.Persistence/ServiceControl.Persistence.csproj @@ -2,6 +2,7 @@ net10.0 + enable diff --git a/src/ServiceControl.Persistence/UnitOfWork/FallbackIngestionUnitOfWork.cs b/src/ServiceControl.Persistence/UnitOfWork/FallbackIngestionUnitOfWork.cs index d257a15213..7ba823d5d6 100644 --- a/src/ServiceControl.Persistence/UnitOfWork/FallbackIngestionUnitOfWork.cs +++ b/src/ServiceControl.Persistence/UnitOfWork/FallbackIngestionUnitOfWork.cs @@ -1,5 +1,6 @@ namespace ServiceControl.Persistence.UnitOfWork { + using System; using System.Threading; using System.Threading.Tasks; @@ -8,15 +9,19 @@ // recoverability and monitoring. It can focus on one at a time. class FallbackIngestionUnitOfWork : IngestionUnitOfWorkBase { - IIngestionUnitOfWork primary; - IIngestionUnitOfWork fallback; + readonly IIngestionUnitOfWork primary; + readonly IIngestionUnitOfWork fallback; public FallbackIngestionUnitOfWork(IIngestionUnitOfWork primary, IIngestionUnitOfWork fallback) { - this.primary = primary; - this.fallback = fallback; - Monitoring = primary.Monitoring ?? fallback.Monitoring; - Recoverability = primary.Recoverability ?? fallback.Recoverability; + this.primary = primary ?? throw new ArgumentNullException(nameof(primary)); + this.fallback = fallback ?? throw new ArgumentNullException(nameof(fallback)); + Monitoring = primary.Monitoring + ?? fallback.Monitoring + ?? throw new InvalidOperationException("Fallback unit of work must implement Monitoring"); + Recoverability = primary.Recoverability + ?? fallback.Recoverability + ?? throw new InvalidOperationException("Fallback unit of work must implement Recoverability"); } public override Task Complete(CancellationToken cancellationToken = default) @@ -27,15 +32,8 @@ public override Task Complete(CancellationToken cancellationToken = default) protected override async ValueTask DisposeAsyncCore() { - if (primary != null) - { - await primary.DisposeAsync(); - } - - if (fallback != null) - { - await fallback.DisposeAsync(); - } + await primary.DisposeAsync(); + await fallback.DisposeAsync(); } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/UnitOfWork/IIngestionUnitOfWork.cs b/src/ServiceControl.Persistence/UnitOfWork/IIngestionUnitOfWork.cs index befc0c6280..0971aaaaab 100644 --- a/src/ServiceControl.Persistence/UnitOfWork/IIngestionUnitOfWork.cs +++ b/src/ServiceControl.Persistence/UnitOfWork/IIngestionUnitOfWork.cs @@ -6,8 +6,8 @@ public interface IIngestionUnitOfWork : IAsyncDisposable { - IMonitoringIngestionUnitOfWork Monitoring { get; } - IRecoverabilityIngestionUnitOfWork Recoverability { get; } + IMonitoringIngestionUnitOfWork? Monitoring { get; } + IRecoverabilityIngestionUnitOfWork? Recoverability { get; } Task Complete(CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/UnitOfWork/IngestionUnitOfWorkBase.cs b/src/ServiceControl.Persistence/UnitOfWork/IngestionUnitOfWorkBase.cs index 5d55c742ac..32431e061c 100644 --- a/src/ServiceControl.Persistence/UnitOfWork/IngestionUnitOfWorkBase.cs +++ b/src/ServiceControl.Persistence/UnitOfWork/IngestionUnitOfWorkBase.cs @@ -17,8 +17,8 @@ public async ValueTask DisposeAsync() GC.SuppressFinalize(this); } - public IMonitoringIngestionUnitOfWork Monitoring { get; protected set; } - public IRecoverabilityIngestionUnitOfWork Recoverability { get; protected set; } + public IMonitoringIngestionUnitOfWork? Monitoring { get; protected set; } + public IRecoverabilityIngestionUnitOfWork? Recoverability { get; protected set; } public virtual Task Complete(CancellationToken cancellationToken = default) => Task.CompletedTask; } } diff --git a/src/ServiceControl.UnitTests/ExternalIntegrations/MessageFailedConverterTests.cs b/src/ServiceControl.UnitTests/ExternalIntegrations/MessageFailedConverterTests.cs index 70742c55b1..0fab87e60b 100644 --- a/src/ServiceControl.UnitTests/ExternalIntegrations/MessageFailedConverterTests.cs +++ b/src/ServiceControl.UnitTests/ExternalIntegrations/MessageFailedConverterTests.cs @@ -134,12 +134,13 @@ public FailedMessage Build() { return new FailedMessage { + UniqueMessageId = Guid.NewGuid().ToString(), ProcessingAttempts = processingAttempts.Select(x => { var messageMetadata = new Dictionary { - {"SendingEndpoint", new EndpointDetails()}, - {"ReceivingEndpoint", new EndpointDetails()} + {"SendingEndpoint", new EndpointDetails { Name = "Sales", Host = "sales-server" }}, + {"ReceivingEndpoint", new EndpointDetails() { Name = "Shipping", Host = "shipping-server" }}, }; if (messageType != null) { diff --git a/src/ServiceControl.UnitTests/MessageFailures/ArchiveScopeAuditTests.cs b/src/ServiceControl.UnitTests/MessageFailures/ArchiveScopeAuditTests.cs index f63deb85cf..c9ece8069a 100644 --- a/src/ServiceControl.UnitTests/MessageFailures/ArchiveScopeAuditTests.cs +++ b/src/ServiceControl.UnitTests/MessageFailures/ArchiveScopeAuditTests.cs @@ -1,6 +1,7 @@ #nullable enable namespace ServiceControl.UnitTests.MessageFailures; +using System; using System.Linq; using System.Threading.Tasks; using NServiceBus.Testing; @@ -50,7 +51,7 @@ public async Task Single_archive_command_carries_the_single_scope() public async Task Archived_message_is_audited_with_the_scope_of_the_originating_operation() { var audit = new RecordingMessageActionAuditLog(); - var store = new AsyncRangeAndQueueAuditTests.StubErrorMessageDataStore { ErrorByResult = new FailedMessage { Status = FailedMessageStatus.Unresolved } }; + var store = new AsyncRangeAndQueueAuditTests.StubErrorMessageDataStore { ErrorByResult = new FailedMessage { UniqueMessageId = Guid.NewGuid().ToString(), Status = FailedMessageStatus.Unresolved } }; var handler = new ArchiveMessageHandler(store, store, new FakeDomainEvents(), audit); var context = new TestableMessageHandlerContext diff --git a/src/ServiceControl.UnitTests/MessageFailures/AsyncRangeAndQueueAuditTests.cs b/src/ServiceControl.UnitTests/MessageFailures/AsyncRangeAndQueueAuditTests.cs index 01fc61ccaf..08bd8e2a13 100644 --- a/src/ServiceControl.UnitTests/MessageFailures/AsyncRangeAndQueueAuditTests.cs +++ b/src/ServiceControl.UnitTests/MessageFailures/AsyncRangeAndQueueAuditTests.cs @@ -84,7 +84,7 @@ public async Task PendingRetries_by_ids_forwards_attribution_to_the_staged_retry public async Task ArchiveMessage_audits_the_archived_message() { var audit = new RecordingMessageActionAuditLog(); - var store = new StubErrorMessageDataStore { ErrorByResult = new FailedMessage { Status = FailedMessageStatus.Unresolved } }; + var store = new StubErrorMessageDataStore { ErrorByResult = new FailedMessage { UniqueMessageId = Guid.NewGuid().ToString(), Status = FailedMessageStatus.Unresolved } }; var handler = new ArchiveMessageHandler(store, store, new FakeDomainEvents(), audit); var context = new TestableMessageHandlerContext { MessageHeaders = StampedHeaders("op-a") }; @@ -104,7 +104,7 @@ public async Task ArchiveMessage_audits_the_archived_message() public async Task ArchiveMessage_already_archived_is_not_audited() { var audit = new RecordingMessageActionAuditLog(); - var store = new StubErrorMessageDataStore { ErrorByResult = new FailedMessage { Status = FailedMessageStatus.Archived } }; + var store = new StubErrorMessageDataStore { ErrorByResult = new FailedMessage { UniqueMessageId = Guid.NewGuid().ToString(), Status = FailedMessageStatus.Archived } }; var handler = new ArchiveMessageHandler(store, store, new FakeDomainEvents(), audit); var context = new TestableMessageHandlerContext { MessageHeaders = StampedHeaders("op-a") }; @@ -157,7 +157,10 @@ internal sealed class StubErrorMessageDataStore : IFailedMessageQueryDataStore, public string[] RetryPendingMessagesResult { get; set; } = []; public string[] UnArchiveByRangeResult { get; set; } = []; public string[] UnArchiveMessagesResult { get; set; } = []; - public FailedMessage ErrorByResult { get; set; } = new(); + public FailedMessage ErrorByResult { get; set; } = new() + { + UniqueMessageId = Guid.NewGuid().ToString(), + }; public Task GetRetryPendingMessages(DateTime from, DateTime to, string queueAddress, CancellationToken cancellationToken = default) => Task.FromResult(RetryPendingMessagesResult); public Task RemoveFailedMessageRetry(string uniqueMessageId, CancellationToken cancellationToken = default) => Task.CompletedTask; diff --git a/src/ServiceControl.UnitTests/MessageFailures/EditFailedMessagesControllerAuditTests.cs b/src/ServiceControl.UnitTests/MessageFailures/EditFailedMessagesControllerAuditTests.cs index 434a1b83c1..3642c9d510 100644 --- a/src/ServiceControl.UnitTests/MessageFailures/EditFailedMessagesControllerAuditTests.cs +++ b/src/ServiceControl.UnitTests/MessageFailures/EditFailedMessagesControllerAuditTests.cs @@ -34,7 +34,7 @@ static EditFailedMessagesController Create(StubErrorMessageDataStore store, Reco public async Task Edit_emits_single_operation() { var audit = new RecordingMessageActionAuditLog(); - var store = new StubErrorMessageDataStore { ErrorByResult = new FailedMessage { ProcessingAttempts = { new FailedMessage.ProcessingAttempt() } } }; + var store = new StubErrorMessageDataStore { ErrorByResult = new FailedMessage { UniqueMessageId = Guid.NewGuid().ToString(), ProcessingAttempts = { new FailedMessage.ProcessingAttempt() } } }; await Create(store, audit).Edit("msg-1", ValidEdit()); diff --git a/src/ServiceControl.UnitTests/Monitoring/HeartbeatEndpointSettingsSyncHostedServiceTests.cs b/src/ServiceControl.UnitTests/Monitoring/HeartbeatEndpointSettingsSyncHostedServiceTests.cs index 29acd797af..e64a34daee 100644 --- a/src/ServiceControl.UnitTests/Monitoring/HeartbeatEndpointSettingsSyncHostedServiceTests.cs +++ b/src/ServiceControl.UnitTests/Monitoring/HeartbeatEndpointSettingsSyncHostedServiceTests.cs @@ -148,7 +148,7 @@ public async Task Guid instanceB = DeterministicGuid.MakeId(endpointName1, "B"); Guid instanceC = DeterministicGuid.MakeId(endpointName1, "C"); var mockMonitoringDataStore = new MockMonitoringDataStore( - [new KnownEndpoint { EndpointDetails = new EndpointDetails { Name = endpointName1 } }]); + [new KnownEndpoint { EndpointDetails = new EndpointDetails { Name = endpointName1, Host = endpointName1 } }]); var mockEndpointInstanceMonitoring = new MockEndpointInstanceMonitoring([ new EndpointsView { IsSendingHeartbeats = false, Name = endpointName1, Id = instanceA }, new EndpointsView { IsSendingHeartbeats = false, Name = endpointName1, Id = instanceB }, @@ -189,7 +189,7 @@ public async Task const string endpointName1 = "Sales"; Guid instanceA = DeterministicGuid.MakeId(endpointName1, "A"); var mockMonitoringDataStore = new MockMonitoringDataStore( - [new KnownEndpoint { EndpointDetails = new EndpointDetails { Name = endpointName1 } }]); + [new KnownEndpoint { EndpointDetails = new EndpointDetails { Name = endpointName1, Host = endpointName1 } }]); var mockEndpointInstanceMonitoring = new MockEndpointInstanceMonitoring([ new EndpointsView { IsSendingHeartbeats = false, Name = endpointName1, Id = instanceA }, new EndpointsView diff --git a/src/ServiceControl/Operations/EndpointDetailsParser.cs b/src/ServiceControl/Operations/EndpointDetailsParser.cs index 7801a0fe1e..4bc4e1eb5d 100644 --- a/src/ServiceControl/Operations/EndpointDetailsParser.cs +++ b/src/ServiceControl/Operations/EndpointDetailsParser.cs @@ -11,7 +11,7 @@ class EndpointDetailsParser { public static EndpointDetails SendingEndpoint(IReadOnlyDictionary headers) { - var endpointDetails = new EndpointDetails(); + var endpointDetails = new EndpointDetails() { Name = "", Host = "" }; DictionaryExtensions.CheckIfKeyExists(Headers.OriginatingEndpoint, headers, s => endpointDetails.Name = s); DictionaryExtensions.CheckIfKeyExists(Headers.OriginatingMachine, headers, s => endpointDetails.Host = s); @@ -38,7 +38,7 @@ public static EndpointDetails SendingEndpoint(IReadOnlyDictionary headers) { - var endpoint = new EndpointDetails(); + var endpoint = new EndpointDetails() { Name = "", Host = "" }; if (headers.TryGetValue(Headers.HostId, out var hostIdHeader)) { From 856cca580687486965b99100a0974c38ba9fa5c5 Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Fri, 14 Aug 2026 15:24:34 +0800 Subject: [PATCH 2/3] Updates from code review --- .../Implementation/EventLogDataStore.cs | 2 +- .../FailedErrorImportDataStore.cs | 4 +-- .../Implementation/FailedMessageViewMapper.cs | 12 +++---- .../EFCore/PersistenceTestsContext.cs | 2 +- .../Operations/EndpointDetailsParser.cs | 33 +++++++------------ 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs index 82ec32b9d9..db2f53e444 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs @@ -13,7 +13,7 @@ public Task Add(EventLogItem logItem, CancellationToken cancellationToken = defa { dbContext.EventLogItems.Add(new EventLogItemEntity { - Description = logItem.Description ?? "", + Description = logItem.Description ?? string.Empty, Severity = logItem.Severity, RaisedAt = logItem.RaisedAt, RelatedTo = logItem.RelatedTo ?? [], diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs index be1c95ffc1..ed204a5a00 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs @@ -54,7 +54,7 @@ public Task StoreFailedErrorImport(FailedErrorImport failure, CancellationToken HeadersJson = headersJson, Body = storedBody, BodyStoredExternally = storeExternally, - ExceptionInfo = failure.ExceptionInfo ?? "" + ExceptionInfo = failure.ExceptionInfo ?? string.Empty }, (entity) => { entity.FailedAt = failedAt; @@ -62,7 +62,7 @@ public Task StoreFailedErrorImport(FailedErrorImport failure, CancellationToken entity.HeadersJson = headersJson; entity.Body = storedBody; entity.BodyStoredExternally = storeExternally; - entity.ExceptionInfo = failure.ExceptionInfo ?? ""; + entity.ExceptionInfo = failure.ExceptionInfo ?? string.Empty; }, token); }, cancellationToken); diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs index f50af192bd..b6b5b280ff 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewMapper.cs @@ -127,22 +127,22 @@ static ExceptionDetails ToExceptionDetails(this FailedMessageEntity entity, Dict }; public static EndpointDetails? ToSendingEndpoint(this FailedMessageEntity entity) => - entity.SendingEndpointName == null && entity.SendingEndpointHost == null + entity.SendingEndpointName == null ? null : new EndpointDetails { - Name = entity.SendingEndpointName ?? "", - Host = entity.SendingEndpointHost ?? "", + Name = entity.SendingEndpointName, + Host = entity.SendingEndpointHost ?? string.Empty, HostId = entity.SendingEndpointHostId ?? Guid.Empty }; public static EndpointDetails? ToReceivingEndpoint(this FailedMessageEntity entity) => - entity.ReceivingEndpointName == null && entity.ReceivingEndpointHost == null + entity.ReceivingEndpointName == null ? null : new EndpointDetails { - Name = entity.ReceivingEndpointName ?? "", - Host = entity.ReceivingEndpointHost ?? "", + Name = entity.ReceivingEndpointName, + Host = entity.ReceivingEndpointHost ?? string.Empty, HostId = entity.ReceivingEndpointHostId ?? Guid.Empty }; diff --git a/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs b/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs index 785c7aacb3..17de14b2dd 100644 --- a/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs +++ b/src/ServiceControl.Persistence.Tests/EFCore/PersistenceTestsContext.cs @@ -51,7 +51,7 @@ static async Task InsertFailedMessagesDirect(IServiceProvider serviceProvider, F var contentType = attempt.Headers.GetValueOrDefault(Headers.ContentType, "text/plain"); db.FailedMessages.Add(new FailedMessageEntity { - UniqueMessageId = Guid.Parse(failedMessage.UniqueMessageId!), + UniqueMessageId = Guid.Parse(failedMessage.UniqueMessageId), FirstTimeOfFailure = ordered.Min(pa => pa.FailureDetails.TimeOfFailure), LastTimeOfFailure = ordered.Max(pa => pa.FailureDetails.TimeOfFailure), LastAttemptedAt = attempt.AttemptedAt, diff --git a/src/ServiceControl/Operations/EndpointDetailsParser.cs b/src/ServiceControl/Operations/EndpointDetailsParser.cs index 4bc4e1eb5d..74eea42a36 100644 --- a/src/ServiceControl/Operations/EndpointDetailsParser.cs +++ b/src/ServiceControl/Operations/EndpointDetailsParser.cs @@ -11,11 +11,12 @@ class EndpointDetailsParser { public static EndpointDetails SendingEndpoint(IReadOnlyDictionary headers) { - var endpointDetails = new EndpointDetails() { Name = "", Host = "" }; - - DictionaryExtensions.CheckIfKeyExists(Headers.OriginatingEndpoint, headers, s => endpointDetails.Name = s); - DictionaryExtensions.CheckIfKeyExists(Headers.OriginatingMachine, headers, s => endpointDetails.Host = s); - DictionaryExtensions.CheckIfKeyExists(Headers.OriginatingHostId, headers, s => endpointDetails.HostId = Guid.Parse(s)); + var endpointDetails = new EndpointDetails() + { + Name = headers.GetValueOrDefault(Headers.OriginatingHostId, string.Empty), + Host = headers.GetValueOrDefault(Headers.OriginatingMachine, string.Empty), + HostId = Guid.TryParse(headers.GetValueOrDefault(Headers.OriginatingHostId, string.Empty), out var g) ? g : Guid.Empty + }; if (!string.IsNullOrEmpty(endpointDetails.Name) && !string.IsNullOrEmpty(endpointDetails.Host)) { @@ -38,23 +39,13 @@ public static EndpointDetails SendingEndpoint(IReadOnlyDictionary headers) { - var endpoint = new EndpointDetails() { Name = "", Host = "" }; - - if (headers.TryGetValue(Headers.HostId, out var hostIdHeader)) - { - endpoint.HostId = Guid.Parse(hostIdHeader); - } - - if (headers.TryGetValue(Headers.HostDisplayName, out var hostDisplayNameHeader)) - { - endpoint.Host = hostDisplayNameHeader; - } - else + var endpoint = new EndpointDetails() { - DictionaryExtensions.CheckIfKeyExists(Headers.ProcessingMachine, headers, s => endpoint.Host = s); - } - - DictionaryExtensions.CheckIfKeyExists(Headers.ProcessingEndpoint, headers, s => endpoint.Name = s); + Name = headers.GetValueOrDefault(Headers.ProcessingEndpoint, string.Empty), + Host = headers.GetValueOrDefault(Headers.HostDisplayName, null) + ?? headers.GetValueOrDefault(Headers.ProcessingMachine, string.Empty), + HostId = Guid.TryParse(headers.GetValueOrDefault(Headers.HostId, string.Empty), out var g) ? g : Guid.Empty + }; if (!string.IsNullOrEmpty(endpoint.Name) && !string.IsNullOrEmpty(endpoint.Host)) { From c8c5c5f9cc9cf56a22c5013feacf87eb5a3a1b85 Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Fri, 14 Aug 2026 16:02:31 +0800 Subject: [PATCH 3/3] Fixes for failing tests --- src/ServiceControl.Persistence.Tests/IngestedFailure.cs | 1 + src/ServiceControl.Persistence/EventLog/EventLogItem.cs | 2 +- .../ExternalIntegrationDispatchRequest.cs | 2 +- src/ServiceControl.Persistence/FailedMessage.cs | 9 +++++++-- src/ServiceControl/Operations/EndpointDetailsParser.cs | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ServiceControl.Persistence.Tests/IngestedFailure.cs b/src/ServiceControl.Persistence.Tests/IngestedFailure.cs index 12d1d48696..2de41ceb33 100644 --- a/src/ServiceControl.Persistence.Tests/IngestedFailure.cs +++ b/src/ServiceControl.Persistence.Tests/IngestedFailure.cs @@ -153,6 +153,7 @@ public FailedMessage ToFailedMessage(FailedMessageStatus status = FailedMessageS // stores the document under it while the relational persisters ignore it. return new FailedMessage { + Id = Guid.NewGuid().ToString(), UniqueMessageId = UniqueMessageIdString, Status = status, ProcessingAttempts = attempts, diff --git a/src/ServiceControl.Persistence/EventLog/EventLogItem.cs b/src/ServiceControl.Persistence/EventLog/EventLogItem.cs index 2d73d06aa1..3328a80648 100644 --- a/src/ServiceControl.Persistence/EventLog/EventLogItem.cs +++ b/src/ServiceControl.Persistence/EventLog/EventLogItem.cs @@ -15,7 +15,7 @@ public class EventLogItem /// /// This could be the Id of a related document, such as the FailedMessage event, which will have more information regarding this alert. /// - public List RelatedTo { get; set; } = []; + public List? RelatedTo { get; set; } public required string Category { get; set; } public required string EventType { get; set; } } diff --git a/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs b/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs index 94a78a27d3..d81329aaa0 100644 --- a/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs +++ b/src/ServiceControl.Persistence/ExternalIntegrations/ExternalIntegrationDispatchRequest.cs @@ -2,7 +2,7 @@ namespace ServiceControl.ExternalIntegrations { public class ExternalIntegrationDispatchRequest { - public string? Id { get; set; } + public required string Id { get; set; } public required object DispatchContext; } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/FailedMessage.cs b/src/ServiceControl.Persistence/FailedMessage.cs index d8a1285fb3..e629816527 100644 --- a/src/ServiceControl.Persistence/FailedMessage.cs +++ b/src/ServiceControl.Persistence/FailedMessage.cs @@ -8,16 +8,21 @@ public class FailedMessage : IHaveStatus { public FailedMessage() { + // these ID fields *should* be marked as required + // but there seem to be some wire usages of this type for + // deserialisation that omit the UniqueMessageId on output + Id = string.Empty; + UniqueMessageId = string.Empty; ProcessingAttempts = []; FailureGroups = []; } - public string? Id { get; set; } + public required string Id { get; set; } + public string UniqueMessageId { get; set; } public List ProcessingAttempts { get; set; } public List FailureGroups { get; set; } - public required string UniqueMessageId { get; set; } public FailedMessageStatus Status { get; set; } diff --git a/src/ServiceControl/Operations/EndpointDetailsParser.cs b/src/ServiceControl/Operations/EndpointDetailsParser.cs index 74eea42a36..22361bd382 100644 --- a/src/ServiceControl/Operations/EndpointDetailsParser.cs +++ b/src/ServiceControl/Operations/EndpointDetailsParser.cs @@ -13,7 +13,7 @@ public static EndpointDetails SendingEndpoint(IReadOnlyDictionary