-
Notifications
You must be signed in to change notification settings - Fork 574
Populating 'instantiates' field of CapabilityStatement. #5241
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
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
abiisnn
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.
🍒
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/SmartV2InstantiateCapability.cs
Fixed
Show fixed
Hide fixed
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs
Fixed
Show fixed
Hide fixed
src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
Show resolved
Hide resolved
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/USCore6InstantiateCapability.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/USCore6InstantiateCapability.cs
Outdated
Show resolved
Hide resolved
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
src/Microsoft.Health.Fhir.Core/Features/Conformance/IVolatileProvideCapability.cs
Show resolved
Hide resolved
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/IInstantiateCapability.cs
Show resolved
Hide resolved
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs
Outdated
Show resolved
Hide resolved
| Assert.Equal(count > 0, urls?.Any()); | ||
| if (count > 0) | ||
| { | ||
| Assert.Equal(Url, urls.FirstOrDefault(), StringComparer.OrdinalIgnoreCase); |
Check warning
Code scanning / CodeQL
Dereferenced variable may be null Warning
urls
this
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 hour ago
In general, to fix a possible null dereference, either ensure the value is non-null before use (by checking and returning/throwing if null) or normalize it to a non-null value (e.g., use an empty collection instead of null) before further operations. Here, the simplest non-invasive fix inside the test is to ensure urls is non-null before calling FirstOrDefault().
The best way to fix this without changing existing functionality is to introduce a local variable that is guaranteed non-null by coalescing urls to an empty sequence when it is null, and then use that variable in the assertion. Specifically, right before the if (count > 0) block, add a line such as var nonNullUrls = urls ?? Enumerable.Empty<string>(); and then replace urls.FirstOrDefault() with nonNullUrls.FirstOrDefault(). Because System.Linq is already imported, no additional imports are needed. This keeps the existing logic intact, avoids any NullReferenceException, and only touches the test code in USCore6InstantiateCapabilityTests.cs where the issue is flagged.
-
Copy modified line R89 -
Copy modified line R92
| @@ -86,9 +86,10 @@ | ||
| var urls = await _capability.GetCanonicalUrlsAsync(CancellationToken.None); | ||
|
|
||
| Assert.Equal(count > 0, urls?.Any()); | ||
| var nonNullUrls = urls ?? Enumerable.Empty<string>(); | ||
| if (count > 0) | ||
| { | ||
| Assert.Equal(Url, urls.FirstOrDefault(), StringComparer.OrdinalIgnoreCase); | ||
| Assert.Equal(Url, nonNullUrls.FirstOrDefault(), StringComparer.OrdinalIgnoreCase); | ||
| } | ||
|
|
||
| Assert.Equal(KnownResourceTypes.StructureDefinition, resourceType, StringComparer.OrdinalIgnoreCase); |
| catch (Exception e) | ||
| { | ||
| _logger.LogWarning(e, "Failed to update Capability Statement."); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 hour ago
In general, this should be fixed by avoiding a blanket catch (Exception) and instead catching more specific exception types that represent expected, recoverable errors, while allowing cancellation and truly critical exceptions to propagate. At a minimum, we should not swallow OperationCanceledException/TaskCanceledException, and optionally we can rethrow OutOfMemoryException, StackOverflowException, etc., but those are rarely caught in typical application code.
For this specific code, the safest change that preserves existing behavior is to (1) explicitly catch and rethrow OperationCanceledException and TaskCanceledException so that cancellation is respected, and (2) catch the remaining Exception types as a secondary catch, log them, and continue as before. That keeps the “best-effort per provider” behavior while improving correctness and satisfying the static analysis requirement by not having a single, generic catch that captures all exceptions indiscriminately.
Concretely, in SystemConformanceProvider within the inner loop over providers, replace the current catch (Exception e) block with two catch blocks: one for cancellation exceptions that rethrow, followed by a narrower catch (Exception e) that only applies to non-cancellation exceptions. This change is all within the shown snippet in src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs and requires no new imports or other definitions.
-
Copy modified lines R396-R405
| @@ -393,6 +393,16 @@ | ||
| _logger.LogInformation("SystemConformanceProvider: Updating the metadata with '{ProviderName}'.", provider.ToString()); | ||
| await provider.UpdateAsync(_builder, cancellationToken); | ||
| } | ||
| catch (OperationCanceledException) | ||
| { | ||
| // Respect cancellation requests and propagate them to the caller. | ||
| throw; | ||
| } | ||
| catch (TaskCanceledException) | ||
| { | ||
| // Respect task cancellations and propagate them to the caller. | ||
| throw; | ||
| } | ||
| catch (Exception e) | ||
| { | ||
| _logger.LogWarning(e, "Failed to update Capability Statement."); |
| catch (Exception ex) | ||
| { | ||
| _logger.LogWarning(ex, "Failed to update the metadata."); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 hour ago
In general, the fix is to replace broad catch (Exception) clauses with more targeted exception handling, only catching exceptions that are expected and can be handled locally. Cancellation and fatal exceptions should be allowed to propagate. For this method, there are two nested generic catches: one around each provider’s UpdateAsync invocation and one around the overall metadata update.
The minimal-impact fix is:
- In the inner loop (per provider), explicitly rethrow
OperationCanceledExceptionso that cancellation behaves correctly, and keep logging other exceptions as warnings. - In the outer
tryaround the whole update operation, also rethrowOperationCanceledExceptionand narrow the remaining catch toExceptionbut with cancellation excluded. We still log and continue for unexpected but non-fatal exceptions, preserving current behavior for most errors while no longer swallowing cancellation.
Concretely, in SystemConformanceProvider in the shown region:
- Modify the inner
catch (Exception e)block (lines 396–399) to checkif (e is OperationCanceledException)and rethrow; otherwise log the warning as today. - Modify the outer
catch (Exception ex)block (lines 409–412) similarly: if the exception is anOperationCanceledException, rethrow; else log the warning as before.
No new imports or types are required; OperationCanceledException is already available from System (line 6) and System.Threading (line 11).
-
Copy modified lines R398-R402 -
Copy modified lines R416-R420
| @@ -395,6 +395,11 @@ | ||
| } | ||
| catch (Exception e) | ||
| { | ||
| if (e is OperationCanceledException) | ||
| { | ||
| throw; | ||
| } | ||
|
|
||
| _logger.LogWarning(e, "Failed to update Capability Statement."); | ||
| } | ||
| finally | ||
| @@ -408,6 +413,11 @@ | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if (ex is OperationCanceledException) | ||
| { | ||
| throw; | ||
| } | ||
|
|
||
| _logger.LogWarning(ex, "Failed to update the metadata."); | ||
| } | ||
| finally |
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Failed to get canonical urls from '{Capability}'.", capability.GetType().Name); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 hour ago
In general, the fix is to avoid catching all Exceptions and instead either (a) catch only specific, expected exception types, or (b) use a when filter to let critical exceptions propagate while still logging and swallowing ordinary ones. In this context, we want to keep the behavior that one failing capability does not prevent others from being processed, but we should not interfere with cancellation or extremely severe runtime errors.
The best targeted fix is to add an exception filter that rethrows on cancellation and on exceptions that are generally considered “critical” (like OutOfMemoryException, StackOverflowException, etc.), while maintaining the current logging-and-continue behavior for all other exceptions. Concretely, in GetUrlsAsync in InstantiatesCapabilityProvider.cs, we replace:
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get canonical urls from '{Capability}'.", capability.GetType().Name);
}with:
catch (Exception ex) when (!IsOperationCanceled(ex) && !IsCriticalException(ex))
{
_logger.LogError(ex, "Failed to get canonical urls from '{Capability}'.", capability.GetType().Name);
}and add a small private helper method (within the same class) that determines whether an exception is an OperationCanceledException (or wraps one) and a helper that checks for critical exception types. No new imports are needed because Exception, OperationCanceledException, OutOfMemoryException, etc. are already in System. This keeps existing functionality for normal failures (they are logged and ignored) but allows cancellation and critical failures to surface as they should.
-
Copy modified lines R54-R67 -
Copy modified line R91
| @@ -51,6 +51,20 @@ | ||
| }); | ||
| } | ||
|
|
||
| private static bool IsOperationCanceled(Exception exception) | ||
| { | ||
| return exception is OperationCanceledException | ||
| || exception.InnerException is OperationCanceledException; | ||
| } | ||
|
|
||
| private static bool IsCriticalException(Exception exception) | ||
| { | ||
| return exception is OutOfMemoryException | ||
| || exception is StackOverflowException | ||
| || exception is ThreadAbortException | ||
| || exception is AccessViolationException; | ||
| } | ||
|
|
||
| private async Task<HashSet<string>> GetUrlsAsync(CancellationToken cancellationToken) | ||
| { | ||
| var capabilityUrls = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
| @@ -74,7 +88,7 @@ | ||
|
|
||
| _logger.LogInformation("{Count} canonical urls added.", urls?.Count ?? 0); | ||
| } | ||
| catch (Exception ex) | ||
| catch (Exception ex) when (!IsOperationCanceled(ex) && !IsCriticalException(ex)) | ||
| { | ||
| _logger.LogError(ex, "Failed to get canonical urls from '{Capability}'.", capability.GetType().Name); | ||
| } |
Description
The change will populate 'instantiates' field of CapabilityStatement when necessary.
Related issues
Addresses [issue #95785].
User Story 95785: Instantiate field and export endpoint is added under Capability statement
Testing
Tested manually and also by adding some UTs.
FHIR Team Checklist
Semver Change (docs)
Patch|Skip|Feature|Breaking (reason)