Skip to content

Conversation

@v-isyamauchi-gh
Copy link
Contributor

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

  • Update the title of the PR to be succinct and less than 65 characters
  • Add a milestone to the PR for the sprint that it is merged (i.e. add S47)
  • Tag the PR with the type of update: Bug, Build, Dependencies, Enhancement, New-Feature or Documentation
  • Tag the PR with Open source, Azure API for FHIR (CosmosDB or common code) or Azure Healthcare APIs (SQL or common code) to specify where this change is intended to be released.
  • Tag the PR with Schema Version backward compatible or Schema Version backward incompatible or Schema Version unchanged if this adds or updates Sql script which is/is not backward compatible with the code.
  • When changing or adding behavior, if your code modifies the system design or changes design assumptions, please create and include an ADR.
  • CI is green before merge Build Status
  • Review squash-merge requirements

Semver Change (docs)

Patch|Skip|Feature|Breaking (reason)

@v-isyamauchi-gh v-isyamauchi-gh added this to the CY25Q3/2Wk07 milestone Nov 17, 2025
@v-isyamauchi-gh v-isyamauchi-gh requested a review from a team as a code owner November 17, 2025 23:24
@v-isyamauchi-gh v-isyamauchi-gh added New Feature Label for a new feature in FHIR OSS Azure API for FHIR Label denotes that the issue or PR is relevant to the Azure API for FHIR Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs Open source This change is only relevant to the OSS code or release. labels Nov 17, 2025
@v-isyamauchi-gh
Copy link
Contributor Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

abiisnn
abiisnn previously approved these changes Nov 25, 2025
Copy link
Contributor

@abiisnn abiisnn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍒

@v-isyamauchi-gh
Copy link
Contributor Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

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

Variable
urls
may be null at this access as suggested by
this
null check.

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.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Conformance/USCore6InstantiateCapabilityTests.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Conformance/USCore6InstantiateCapabilityTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Conformance/USCore6InstantiateCapabilityTests.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Conformance/USCore6InstantiateCapabilityTests.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Conformance/USCore6InstantiateCapabilityTests.cs
@@ -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);
EOF
@@ -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);
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +396 to +399
catch (Exception e)
{
_logger.LogWarning(e, "Failed to update Capability Statement.");
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

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.

Suggested changeset 1
src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs b/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
--- a/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
+++ b/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
@@ -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.");
EOF
@@ -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.");
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +409 to +412
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to update the metadata.");
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

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 OperationCanceledException so that cancellation behaves correctly, and keep logging other exceptions as warnings.
  • In the outer try around the whole update operation, also rethrow OperationCanceledException and narrow the remaining catch to Exception but 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 check if (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 an OperationCanceledException, 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).

Suggested changeset 1
src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs b/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
--- a/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
+++ b/src/Microsoft.Health.Fhir.Core/Features/Conformance/SystemConformanceProvider.cs
@@ -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
EOF
@@ -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
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +77 to +80
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

Generic catch clause.

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.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Conformance/InstantiatesCapabilityProvider.cs
@@ -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);
                     }
EOF
@@ -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);
}
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Azure API for FHIR Label denotes that the issue or PR is relevant to the Azure API for FHIR Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs New Feature Label for a new feature in FHIR OSS No-PaaS-breaking-change Open source This change is only relevant to the OSS code or release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants