-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Refactor configuration for azure queue service for events to include queue name #6724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Claude finished @brant-livefront's task in 1m 36s —— View job Code Review Complete ✓I've reviewed the PR changes that address the findings from PR #6718. Overall, the implementation correctly addresses the three findings, with some suggestions for improvement. Summary✅ Successfully Addresses:
Key Changes:
Findings❓ Missing Test Coverage (line 95 of EventIntegrationsServiceCollectionExtensions.cs)The validation logic checks both
|
src/Core/AdminConsole/Services/Implementations/AzureQueueEventWriteService.cs
Show resolved
Hide resolved
|
New Issues (2)Checkmarx found the following issues in this Pull Request
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6724 +/- ##
==========================================
+ Coverage 54.10% 54.11% +0.01%
==========================================
Files 1922 1922
Lines 85153 85159 +6
Branches 7608 7610 +2
==========================================
+ Hits 46073 46086 +13
+ Misses 37325 37316 -9
- Partials 1755 1757 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
withinfocus
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude's findings 1 and 6 are needed.
89c456f to
2fba65b
Compare
|
I rebased this onto the event integration service collection refactor (instead of main) because I realized we really should add a test for the queue name before starting the azure queue event write service. Previously, the value was hardcoded so it wasn't part of our checks. Since these have been moved in that branch, I needed to rebase onto it to make changes in the final place. |
withinfocus
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but fix lint.
| { | ||
| public AzureQueueEventWriteService(GlobalSettings globalSettings) : base( | ||
| new QueueClient(globalSettings.Events.ConnectionString, "event"), | ||
| new QueueClient(globalSettings.Events.ConnectionString, globalSettings.Events.QueueName), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ Potential Null Reference Exception
If globalSettings.Events.QueueName is null, this will throw an ArgumentNullException when instantiating the QueueClient, even though the service collection registration checks for null values.
While the registration in AddEventWriteServices (line 94-95 of EventIntegrationsServiceCollectionExtensions.cs) now checks both ConnectionString and QueueName, if someone were to manually instantiate this service or if the registration check fails to prevent it, this would throw.
Recommendation: Add a guard clause or null check with a more descriptive error message:
public AzureQueueEventWriteService(GlobalSettings globalSettings) : base(
new QueueClient(
globalSettings.Events.ConnectionString ?? throw new ArgumentNullException(nameof(globalSettings.Events.ConnectionString)),
globalSettings.Events.QueueName ?? throw new ArgumentNullException(nameof(globalSettings.Events.QueueName))
),
JsonHelpers.IgnoreWritingNull)
{ }This would make debugging easier by clearly identifying which parameter is null.
| public string ConnectionString | ||
| { | ||
| get => _connectionString; | ||
| set => _connectionString = value?.Trim('"'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Good Null Safety Implementation
The null-conditional operator (value?.Trim('"')) correctly handles null values, allowing the QueueName to be null (which is intentional per the author's note to disable the service in production).
This is consistent with the pattern used in other settings classes like AzureServiceBusSettings.ConnectionString (line 326).
| if (string.IsNullOrWhiteSpace(storageConnectionString) || | ||
| string.IsNullOrWhiteSpace(queueName)) | ||
| { | ||
| _logger.LogInformation("Azure Queue Hosted Service is disabled. Missing connection string or queue name."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Excellent Error Handling
The information-level log message clearly indicates why the service is disabled. This is much better than the previous silent failure.
Minor suggestion: Consider using structured logging to make this more queryable:
_logger.LogInformation("Azure Queue Hosted Service is disabled. ConnectionStringPresent: {ConnectionStringPresent}, QueueNamePresent: {QueueNamePresent}",
!string.IsNullOrWhiteSpace(storageConnectionString),
!string.IsNullOrWhiteSpace(queueName));This would help operators understand exactly which configuration is missing without needing to look at code.
| } | ||
|
|
||
| if (CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) | ||
| if (CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Correctly Addresses Previous Review Finding #2
This now checks both ConnectionString and QueueName before registering the Azure Queue service, preventing the hardcoded queue name issue from the previous review.
The check aligns with the documentation in the XML comments (lines 69-70) and the read-side implementation in AzureQueueHostedService.cs (lines 60-63).
| { | ||
| ["GlobalSettings:Events:ConnectionString"] = "DefaultEndpointsProtocol=https;AccountName=test;AccountKey=test;EndpointSuffix=core.windows.net" | ||
| ["GlobalSettings:Events:ConnectionString"] = "DefaultEndpointsProtocol=https;AccountName=test;AccountKey=test;EndpointSuffix=core.windows.net", | ||
| ["GlobalSettings:Events:QueueName"] = "event" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Missing Test Coverage
The author mentioned adding "a test to prevent service start if QueueName is null/empty", but I cannot find such a test in this file.
There should be tests for these scenarios:
- When ConnectionString is present but QueueName is null/empty → Should register NoopEventWriteService
- When QueueName is present but ConnectionString is null/empty → Should register NoopEventWriteService
Recommendation: Add these test cases:
[Fact]
public void AddEventWriteServices_EventsConnectionStringPresentButQueueNameMissing_RegistersNoopService()
{
var services = new ServiceCollection();
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
{
["GlobalSettings:Events:ConnectionString"] = "DefaultEndpointsProtocol=https;AccountName=test;AccountKey=test;EndpointSuffix=core.windows.net",
["GlobalSettings:Events:QueueName"] = null
});
services.AddEventWriteServices(globalSettings);
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(NoopEventWriteService));
}
[Fact]
public void AddEventWriteServices_QueueNamePresentButConnectionStringMissing_RegistersNoopService()
{
var services = new ServiceCollection();
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
{
["GlobalSettings:Events:ConnectionString"] = null,
["GlobalSettings:Events:QueueName"] = "event"
});
services.AddEventWriteServices(globalSettings);
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService) && s.ImplementationType == typeof(NoopEventWriteService));
}| } | ||
| } | ||
|
|
||
| public class AzureQueueEventSettings : IConnectionStringSettings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Note: Inconsistent Null Handling Pattern in Codebase
The new AzureQueueEventSettings class uses null-conditional operators (value?.Trim('"')) in both ConnectionString and QueueName setters (lines 406, 412), which is the safe approach.
However, most other settings classes in this file use value.Trim('"') without null checks (e.g., ConnectionStringSettings at line 423), which will throw NullReferenceException if value is null.
For this PR: The null-safe approach here is correct and aligns with the intentional design of allowing null QueueName to disable the service.
For future consideration: The codebase has an inconsistent pattern that could be standardized in a separate refactoring PR.
| public string ConnectionString | ||
| { | ||
| get => _connectionString; | ||
| set => _connectionString = value?.Trim('"'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good null safety: The null-conditional operator value?.Trim('"') correctly handles null values. However, note that ConnectionStringSettings.ConnectionString (line 423, not changed in this PR) uses value.Trim('"') without the null-conditional operator, creating an inconsistency in the codebase.
While this isn't a blocker for this PR, consider filing a follow-up issue to address the null safety inconsistency in ConnectionStringSettings to maintain consistency across similar settings classes.
|
|
||
| if (CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) | ||
| if (CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString) && | ||
| CoreHelpers.SettingHasValue(globalSettings.Events.QueueName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test coverage: While the implementation correctly validates both ConnectionString and QueueName, there's no test case covering the scenario where ConnectionString is present but QueueName is missing (or vice versa).
Consider adding test cases like:
AddEventWriteServices_ConnectionStringPresentButQueueNameMissing_DoesNotRegisterAzureQueueServiceAddEventWriteServices_QueueNamePresentButConnectionStringMissing_DoesNotRegisterAzureQueueService
This would ensure the validation logic works correctly and prevent regression if someone changes the && to || by mistake.
| { | ||
| public AzureQueueEventWriteService(GlobalSettings globalSettings) : base( | ||
| new QueueClient(globalSettings.Events.ConnectionString, "event"), | ||
| new QueueClient(globalSettings.Events.ConnectionString, globalSettings.Events.QueueName), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential runtime exception if QueueName is null: While AddEventWriteServices now checks for null QueueName before registering this service, this constructor doesn't validate that globalSettings.Events.QueueName is non-null. If this service is ever instantiated directly (outside of DI), it could throw ArgumentNullException from the QueueClient constructor.
Consider adding a guard clause for defensive programming:
public AzureQueueEventWriteService(GlobalSettings globalSettings) : base(
new QueueClient(
globalSettings.Events.ConnectionString
?? throw new ArgumentNullException(nameof(globalSettings), "Events.ConnectionString cannot be null"),
globalSettings.Events.QueueName
?? throw new ArgumentNullException(nameof(globalSettings), "Events.QueueName cannot be null")),
JsonHelpers.IgnoreWritingNull)
{ }This provides better error messages and fail-fast behavior if the service is misconfigured.
| if (string.IsNullOrWhiteSpace(storageConnectionString) || | ||
| string.IsNullOrWhiteSpace(queueName)) | ||
| { | ||
| _logger.LogInformation("Azure Queue Hosted Service is disabled. Missing connection string or queue name."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good improvement: This log message is helpful for troubleshooting configuration issues. The service now gracefully handles missing configuration and logs appropriately.
Minor suggestion: Consider using structured logging with named parameters for better log aggregation and filtering:
_logger.LogInformation("Azure Queue Hosted Service is disabled. ConnectionString present: {HasConnectionString}, QueueName present: {HasQueueName}",
!string.IsNullOrWhiteSpace(storageConnectionString),
!string.IsNullOrWhiteSpace(queueName));This would make it easier to diagnose which specific configuration value is missing in production environments.
| "key": "<your Installation Key>" | ||
| }, | ||
| "events": { | ||
| "connectionString": "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation needed: The example shows queueName: "event" as the default value, but it's not clear:
- Whether this queue name must match exactly in production environments
- What happens if this is left empty (service is disabled, which is intentional per PR author's comment)
- Any naming conventions or restrictions for queue names
Consider adding a comment above this section:
// Azure Queue Storage configuration for events
// Leave connectionString and queueName empty to disable the service in production
// Default queue name is "event" for local development
"events": {This would help developers understand the configuration's purpose and behavior.
| public virtual MailSettings Mail { get; set; } = new MailSettings(); | ||
| public virtual IConnectionStringSettings Storage { get; set; } = new ConnectionStringSettings(); | ||
| public virtual ConnectionStringSettings Events { get; set; } = new ConnectionStringSettings(); | ||
| public virtual AzureQueueEventSettings Events { get; set; } = new AzureQueueEventSettings(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backward compatibility concern: Changing from ConnectionStringSettings to AzureQueueEventSettings is a breaking change in the configuration schema. Existing appsettings.json files across the codebase only have:
"events": {
"connectionString": "SECRET"
}They don't have the new queueName field. While the code handles this gracefully (the service won't register if QueueName is null), this means:
- Existing deployments will break silently - The Azure Queue event write service won't start, and events won't be queued, but there will be no error - just a log message in
AzureQueueHostedService. - All appsettings files need updating - Every environment configuration file needs the
queueNamefield added.
Recommendations:
- Document this breaking change in the PR description
- Provide migration instructions for updating all appsettings files
- Consider whether a default value (e.g., "event") should be used when
queueNameis not specified for backward compatibility - Update all appsettings*.json files that currently have
events.connectionStringto also includequeueName
|
The base branch was changed.
c85201b to
aaf8d48
Compare
|
Rebased and re-targeted to main now that the other PRs have all merged. |
| public string QueueName | ||
| { | ||
| get => _queueName; | ||
| set => _queueName = value?.Trim('"'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Good null safety: The null-conditional operator value?.Trim('"') correctly allows null values, which is intentional for disabling the service in production (as noted by the PR author).
This is consistent with patterns used elsewhere in the codebase like AzureServiceBusSettings.ConnectionString at line 326.
|
|
||
| if (CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) | ||
| if (CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString) && | ||
| CoreHelpers.SettingHasValue(globalSettings.Events.QueueName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Missing test coverage for partial configuration: The check validates both ConnectionString AND QueueName, but there are no tests covering scenarios where only one is present.
Recommended test cases:
[Fact]
public void AddEventWriteServices_ConnectionStringPresentButQueueNameMissing_RegistersNoopService()
{
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
{
["GlobalSettings:Events:ConnectionString"] = "DefaultEndpoints...",
["GlobalSettings:Events:QueueName"] = null
});
services.AddEventWriteServices(globalSettings);
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService)
&& s.ImplementationType == typeof(NoopEventWriteService));
}
[Fact]
public void AddEventWriteServices_QueueNamePresentButConnectionStringMissing_RegistersNoopService()
{
var globalSettings = CreateGlobalSettings(new Dictionary<string, string?>
{
["GlobalSettings:Events:ConnectionString"] = null,
["GlobalSettings:Events:QueueName"] = "event"
});
services.AddEventWriteServices(globalSettings);
Assert.Contains(services, s => s.ServiceType == typeof(IEventWriteService)
&& s.ImplementationType == typeof(NoopEventWriteService));
}This ensures the && logic works correctly and prevents regression.
| { | ||
| public AzureQueueEventWriteService(GlobalSettings globalSettings) : base( | ||
| new QueueClient(globalSettings.Events.ConnectionString, "event"), | ||
| new QueueClient(globalSettings.Events.ConnectionString, globalSettings.Events.QueueName), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding guard clauses for better error messages:
public AzureQueueEventWriteService(GlobalSettings globalSettings) : base(
new QueueClient(
globalSettings.Events.ConnectionString
?? throw new ArgumentNullException(nameof(globalSettings), "Events.ConnectionString cannot be null"),
globalSettings.Events.QueueName
?? throw new ArgumentNullException(nameof(globalSettings), "Events.QueueName cannot be null")),
JsonHelpers.IgnoreWritingNull)
{ }This provides fail-fast behavior with clear error messages if misconfigured.
| if (string.IsNullOrWhiteSpace(storageConnectionString) || | ||
| string.IsNullOrWhiteSpace(queueName)) | ||
| { | ||
| _logger.LogInformation("Azure Queue Hosted Service is disabled. Missing connection string or queue name."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Excellent error handling: The information-level log message properly addresses Finding #3 from the previous review. This makes troubleshooting much easier.
Minor enhancement suggestion (optional): Consider using structured logging to make this more queryable in production:
_logger.LogInformation(
"Azure Queue Hosted Service is disabled. ConnectionString present: {HasConnectionString}, QueueName present: {HasQueueName}",
!string.IsNullOrWhiteSpace(storageConnectionString),
!string.IsNullOrWhiteSpace(queueName));This would help operators quickly identify which specific configuration value is missing without inspecting code.
| }, | ||
| "events": { | ||
| "connectionString": "", | ||
| "queueName": "event" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Documentation suggestion: While the example shows "event" as the queue name, it's not clear to developers:
- Whether this exact value is required in production
- What happens if left empty (service is disabled)
- Naming conventions or restrictions for queue names
Consider adding a comment:
// Azure Queue Storage configuration for events
// Leave connectionString and queueName empty to disable the service
// Default queue name "event" is used for local development
"events": {
"connectionString": "",
"queueName": "event"
},




📔 Objective
In PR #6718 we added a queue name for the hosted service that reads the events. However, there were a couple of Claude comments (Findings 2, 3 and 4) that still would be good to address. This PR addresses all three:
AzureQueueEventWriteServicenow pulls this from global settingsIConfigurationinstead ofGlobalSettingsglobalSettings.Eventsobject to a custom one and away from ConnectionStringSettings. I've preserved the ConnectionString (even preserved conforming withIConnectionStringSettings) so that it would be backwards compatible. That is,globalSettings.Events.ConnectionStringand any configs that specify it should function just the same. However this effects things outside of just this Queue service (for instanceInstallationDeviceRepository), so it would be good to give it a very close look and test accordingly.⏰ Reminders before review
🦮 Reviewer guidelines
:+1:) or similar for great changes:memo:) or ℹ️ (:information_source:) for notes or general info:question:) for questions:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion:art:) for suggestions / improvements:x:) or:warning:) for more significant problems or concerns needing attention:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt:pick:) for minor or nitpick changes