Skip to content

Conversation

@v-isyamauchi-gh
Copy link
Contributor

@v-isyamauchi-gh v-isyamauchi-gh commented Aug 5, 2025

Description

Implementing a duplicate clinical reference feature (approach 1).

Related issues

Addresses [issue #158013].

User Story 158013: Duplicate url information on 2 properties for DocumentReference

Testing

Tested by adding 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 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 Aug 8, 2025
@v-isyamauchi-gh v-isyamauchi-gh added this to the CY25Q3/2Wk07 milestone Aug 8, 2025
@v-isyamauchi-gh v-isyamauchi-gh marked this pull request as ready for review August 8, 2025 23:01
@v-isyamauchi-gh v-isyamauchi-gh requested a review from a team as a code owner August 8, 2025 23:01
@v-isyamauchi-gh v-isyamauchi-gh changed the title [Do not review] Approach 1 prototype Implementing a duplicate clinical reference (approach 1). Aug 8, 2025
Comment on lines +188 to +199
foreach (var r in duplicateResources)
{
var wrapper = new ResourceWrapper(
r.ToResourceElement(),
new RawResource(r.ToJson(), FhirResourceFormat.Json, false),
null,
false,
null,
null,
null);
entries.Add(new SearchResultEntry(wrapper));
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 2 months ago

To fix the problem, we should replace the foreach loop at line 384 that transforms each element in duplicateResources into a SearchResultEntry containing a ResourceWrapper with a single line that projects the transformation using LINQ's .Select. Specifically, we will use .Select to create a list of SearchResultEntry objects from the duplicateResources collection, and then assign the resulting list to entries.

  • In src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/ClinicalReferenceDuplicatorTests.cs, replace the initialization and population of entries within the conditional block around lines 381-396 with one assignment: entries = duplicateResources.Select(...).ToList();.
  • No new methods or imports are needed since System.Linq is already imported.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/ClinicalReferenceDuplicatorTests.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/Guidance/ClinicalReferenceDuplicatorTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/ClinicalReferenceDuplicatorTests.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/ClinicalReferenceDuplicatorTests.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/ClinicalReferenceDuplicatorTests.cs
@@ -378,22 +378,20 @@
             var duplicateResourceIds = duplicateResources.Select(x => x.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
 
             // Set up a validation on a request for searching duplicate resources.
-            var entries = new List<SearchResultEntry>();
-            if (duplicateResources?.Any() ?? false)
-            {
-                foreach (var r in duplicateResources)
-                {
-                    var wrapper = new ResourceWrapper(
-                        r.ToResourceElement(),
-                        new RawResource(r.ToJson(), FhirResourceFormat.Json, false),
-                        null,
-                        false,
-                        null,
-                        null,
-                        null);
-                    entries.Add(new SearchResultEntry(wrapper));
-                }
-            }
+            List<SearchResultEntry> entries = duplicateResources?.Any() ?? false
+                ? duplicateResources.Select(r =>
+                    {
+                        var wrapper = new ResourceWrapper(
+                            r.ToResourceElement(),
+                            new RawResource(r.ToJson(), FhirResourceFormat.Json, false),
+                            null,
+                            false,
+                            null,
+                            null,
+                            null);
+                        return new SearchResultEntry(wrapper);
+                    }).ToList()
+                : new List<SearchResultEntry>();
 
             _searchService.SearchAsync(
                 Arg.Is<string>(x => string.Equals(x, duplicateResourceType, StringComparison.OrdinalIgnoreCase)),
EOF
@@ -378,22 +378,20 @@
var duplicateResourceIds = duplicateResources.Select(x => x.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);

// Set up a validation on a request for searching duplicate resources.
var entries = new List<SearchResultEntry>();
if (duplicateResources?.Any() ?? false)
{
foreach (var r in duplicateResources)
{
var wrapper = new ResourceWrapper(
r.ToResourceElement(),
new RawResource(r.ToJson(), FhirResourceFormat.Json, false),
null,
false,
null,
null,
null);
entries.Add(new SearchResultEntry(wrapper));
}
}
List<SearchResultEntry> entries = duplicateResources?.Any() ?? false
? duplicateResources.Select(r =>
{
var wrapper = new ResourceWrapper(
r.ToResourceElement(),
new RawResource(r.ToJson(), FhirResourceFormat.Json, false),
null,
false,
null,
null,
null);
return new SearchResultEntry(wrapper);
}).ToList()
: new List<SearchResultEntry>();

_searchService.SearchAsync(
Arg.Is<string>(x => string.Equals(x, duplicateResourceType, StringComparison.OrdinalIgnoreCase)),
Copilot is powered by AI and may make mistakes. Always verify output.
};

var resourceElement = resource.ToResourceElement();
var resourceWrapper = CreateResourceWrapper(resourceElement);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

This assignment to
resourceWrapper
is useless, since its value is never read.

Copilot Autofix

AI 2 months ago

The best way to address this problem is to remove the assignment to resourceWrapper on line 149. Since the method's returned value is unused, and there's no evidence in the code that CreateResourceWrapper(resourceElement) must be called for necessary side effects, the assignment and the function call can be safely deleted.

Specifically, in DuplicateClinicalReferenceBehaviorTests.cs, within the method body of GivenDeleteRequest_WhenDuplicateClinicalReferenceIsEnabled_ThenThenDuplicateResourceShouldBeDeleted, simply delete the line:

var resourceWrapper = CreateResourceWrapper(resourceElement);

No imports, method definitions, or variable definitions need to be added or changed.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/DuplicateClinicalReferenceBehaviorTests.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/Guidance/DuplicateClinicalReferenceBehaviorTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/DuplicateClinicalReferenceBehaviorTests.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/DuplicateClinicalReferenceBehaviorTests.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Guidance/DuplicateClinicalReferenceBehaviorTests.cs
@@ -146,7 +146,6 @@
             };
 
             var resourceElement = resource.ToResourceElement();
-            var resourceWrapper = CreateResourceWrapper(resourceElement);
             var request = new DeleteResourceRequest(
                 new ResourceKey(resource.TypeName, resource.Id),
                 DeleteOperation.SoftDelete);
EOF
@@ -146,7 +146,6 @@
};

var resourceElement = resource.ToResourceElement();
var resourceWrapper = CreateResourceWrapper(resourceElement);
var request = new DeleteResourceRequest(
new ResourceKey(resource.TypeName, resource.Id),
DeleteOperation.SoftDelete);
Copilot is powered by AI and may make mistakes. Always verify output.
EnsureArg.IsNotNull(clinicalReferenceDuplicator, nameof(clinicalReferenceDuplicator));
EnsureArg.IsNotNull(logger, nameof(logger));

_coreFeatureConfiguration = coreFeatureConfiguration.Value;

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

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

Copilot Autofix

AI 2 months ago

To properly fix this issue, add an explicit null check for the coreFeatureConfiguration parameter before accessing its .Value property. This can be achieved by using EnsureArg.IsNotNull(coreFeatureConfiguration, nameof(coreFeatureConfiguration)); before the existing check on coreFeatureConfiguration.Value. This ensures that the constructor will fail early and clearly if a null parameter is ever passed, and will not cause a fail-later NullReferenceException. No changes to functionality are introduced; only the missing null parameter guard is added. This change should be applied at the very top of the constructor, specifically in the region of lines 29–31.


Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/DuplicateClinicalReferenceBehavior.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/Guidance/DuplicateClinicalReferenceBehavior.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/DuplicateClinicalReferenceBehavior.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/DuplicateClinicalReferenceBehavior.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/DuplicateClinicalReferenceBehavior.cs
@@ -27,6 +27,7 @@
             IOptions<CoreFeatureConfiguration> coreFeatureConfiguration,
             IClinicalReferenceDuplicator clinicalReferenceDuplicator)
         {
+            EnsureArg.IsNotNull(coreFeatureConfiguration, nameof(coreFeatureConfiguration));
             EnsureArg.IsNotNull(coreFeatureConfiguration?.Value, nameof(coreFeatureConfiguration));
             EnsureArg.IsNotNull(clinicalReferenceDuplicator, nameof(clinicalReferenceDuplicator));
 
EOF
@@ -27,6 +27,7 @@
IOptions<CoreFeatureConfiguration> coreFeatureConfiguration,
IClinicalReferenceDuplicator clinicalReferenceDuplicator)
{
EnsureArg.IsNotNull(coreFeatureConfiguration, nameof(coreFeatureConfiguration));
EnsureArg.IsNotNull(coreFeatureConfiguration?.Value, nameof(coreFeatureConfiguration));
EnsureArg.IsNotNull(clinicalReferenceDuplicator, nameof(clinicalReferenceDuplicator));

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +455 to +462
foreach (var wrapper in resourceWrappersFound)
{
var wrapperUpdated = await UpdateDuplicateResourceInternalAsync(
resource,
wrapper,
cancellationToken);
duplicateResourceWrappers.Add(wrapperUpdated);
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 2 months ago

To fix the problem, refactor the foreach loop at line 455 that accumulates asynchronously-updated wrappers into a single statement using LINQ's Select and Task.WhenAll. Specifically, project the resourceWrappersFound collection to a collection of tasks using Select, then use Task.WhenAll to await all those tasks in parallel, and finally convert the resulting array to a list. Replace the manual foreach loop with this one-liner, assigning the result to duplicateResourceWrappers.

  • Changes are required in src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs, at the block starting at line 455.
  • No extra methods or imports are required; all necessary types (Select, Task.WhenAll, .ToList()) are already available and relevant namespaces are already imported.
  • No side effects are performed inside the loop; no change to program logic or state is expected.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.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/Guidance/ClinicalReferenceDuplicator.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
@@ -452,14 +452,15 @@
                     }
                     else
                     {
-                        foreach (var wrapper in resourceWrappersFound)
-                        {
-                            var wrapperUpdated = await UpdateDuplicateResourceInternalAsync(
-                                resource,
-                                wrapper,
-                                cancellationToken);
-                            duplicateResourceWrappers.Add(wrapperUpdated);
-                        }
+                        duplicateResourceWrappers.AddRange(
+                            (await Task.WhenAll(
+                                resourceWrappersFound
+                                    .Select(wrapper =>
+                                        UpdateDuplicateResourceInternalAsync(
+                                            resource,
+                                            wrapper,
+                                            cancellationToken))))
+                        );
                     }
                 }
                 else
EOF
@@ -452,14 +452,15 @@
}
else
{
foreach (var wrapper in resourceWrappersFound)
{
var wrapperUpdated = await UpdateDuplicateResourceInternalAsync(
resource,
wrapper,
cancellationToken);
duplicateResourceWrappers.Add(wrapperUpdated);
}
duplicateResourceWrappers.AddRange(
(await Task.WhenAll(
resourceWrappersFound
.Select(wrapper =>
UpdateDuplicateResourceInternalAsync(
resource,
wrapper,
cancellationToken))))
);
}
}
else
Copilot is powered by AI and may make mistakes. Always verify output.
};

var crefs = GetClinicalReferences(documentReference);
var codes = crefs.SelectMany(x => x.codes);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

This assignment to
codes
is useless, since its value is never read.

Copilot Autofix

AI 2 months ago

To fix the problem, the assignment to codes should be removed from line 595 in ClinicalReferenceDuplicator.cs. The result of crefs.SelectMany(x => x.codes) is never used via the codes variable; instead, the same expression is used directly in the subsequent foreach loop. Thus, simply delete the line. No further changes are needed. No additional imports, definitions, or method changes are required.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.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/Guidance/ClinicalReferenceDuplicator.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
@@ -592,7 +592,6 @@
                 };
 
                 var crefs = GetClinicalReferences(documentReference);
-                var codes = crefs.SelectMany(x => x.codes);
                 diagnosticReport.Code = new CodeableConcept();
                 foreach (var code in crefs.SelectMany(x => x.codes))
                 {
EOF
@@ -592,7 +592,6 @@
};

var crefs = GetClinicalReferences(documentReference);
var codes = crefs.SelectMany(x => x.codes);
diagnosticReport.Code = new CodeableConcept();
foreach (var code in crefs.SelectMany(x => x.codes))
{
Copilot is powered by AI and may make mistakes. Always verify output.
return documentReference.Content
.SelectMany(x => x?.Profile?
.Where(y => y?.Value?.GetType() == typeof(Coding)
&& ClinicalReferenceSystems.Contains(((Coding)y.Value)?.System)

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

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

Copilot Autofix

AI 2 months ago

To fix the problem, add a null check for y before dereferencing it in the Select clause. The best approach is to check if y is not null before casting y.Value as Coding. This can be done by modifying the lambda expression in the Select statement such that it returns (Coding)y.Value only if y is not null, otherwise returns null, and then filter out nulls before further processing (via Where, for example).

The code to change is in src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs, specifically lines 630-634 for the block under the #else conditional. We will update the SelectMany so that y is checked for null before casting y.Value to Coding, and filter the result to non-null values.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.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/Guidance/ClinicalReferenceDuplicator.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
@@ -628,10 +628,12 @@
 #else
                 return documentReference.Content
                     .SelectMany(x => x?.Profile?
-                        .Where(y => y?.Value?.GetType() == typeof(Coding)
+                        .Where(y => y != null && y?.Value?.GetType() == typeof(Coding)
                             && ClinicalReferenceSystems.Contains(((Coding)y.Value)?.System)
                             && ClinicalReferenceCodes.Contains(((Coding)y.Value)?.Code))
-                        .Select(y => (Coding)y.Value))
+                        .Select(y => y != null ? (Coding)y.Value : null)
+                    )
+                    .Where(x => x != null)
                     .DistinctBy(x => ClinicalReferenceDuplicatorHelper.ConvertToString(x))
                     .ToList();
 #endif
EOF
@@ -628,10 +628,12 @@
#else
return documentReference.Content
.SelectMany(x => x?.Profile?
.Where(y => y?.Value?.GetType() == typeof(Coding)
.Where(y => y != null && y?.Value?.GetType() == typeof(Coding)
&& ClinicalReferenceSystems.Contains(((Coding)y.Value)?.System)
&& ClinicalReferenceCodes.Contains(((Coding)y.Value)?.Code))
.Select(y => (Coding)y.Value))
.Select(y => y != null ? (Coding)y.Value : null)
)
.Where(x => x != null)
.DistinctBy(x => ClinicalReferenceDuplicatorHelper.ConvertToString(x))
.ToList();
#endif
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +731 to +745
foreach (var attachment in diagnosticReport.PresentedForm)
{
if (!documentReference.Content.Any(
x => ClinicalReferenceDuplicatorHelper.CompareCoding(x.Format, code)
&& ClinicalReferenceDuplicatorHelper.CompareAttachment(x.Attachment, attachment)))
{
documentReference.Content.Add(
new DocumentReference.ContentComponent()
{
Attachment = attachment,
Format = code,
});
resourceUpdated = true;
}
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Where Note

This foreach loop
implicitly filters its target sequence
- consider filtering the sequence explicitly using '.Where(...)'.

Copilot Autofix

AI 2 months ago

To fix the problem, refactor the foreach loop at line 731 so that it iterates only over those attachment elements of diagnosticReport.PresentedForm for which !documentReference.Content.Any(...) holds. This is done by replacing the loop:

foreach (var attachment in diagnosticReport.PresentedForm)
{
    if (!documentReference.Content.Any(...))
    {
        // Loop body
    }
}

with:

foreach (var attachment in diagnosticReport.PresentedForm
    .Where(attachment => !documentReference.Content.Any(
        x => ClinicalReferenceDuplicatorHelper.CompareCoding(x.Format, code)
                  && ClinicalReferenceDuplicatorHelper.CompareAttachment(x.Attachment, attachment))))
{
    // Loop body (without guard if)
}

Inside the rewritten loop, remove the redundant if-statement and directly execute the logic.

Only the code in file src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs, lines around 730–745, needs to be changed.

No new imports, methods, or definitions are needed as LINQ is already imported.


Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.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/Guidance/ClinicalReferenceDuplicator.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
@@ -728,20 +728,18 @@
 #if R4 || R4B || Stu3
                 foreach (var code in GetClinicalReferenceCodes(diagnosticReport))
                 {
-                    foreach (var attachment in diagnosticReport.PresentedForm)
+                    foreach (var attachment in diagnosticReport.PresentedForm
+                        .Where(attachment => !documentReference.Content.Any(
+                            x => ClinicalReferenceDuplicatorHelper.CompareCoding(x.Format, code)
+                                && ClinicalReferenceDuplicatorHelper.CompareAttachment(x.Attachment, attachment))))
                     {
-                        if (!documentReference.Content.Any(
-                                x => ClinicalReferenceDuplicatorHelper.CompareCoding(x.Format, code)
-                                    && ClinicalReferenceDuplicatorHelper.CompareAttachment(x.Attachment, attachment)))
-                        {
-                            documentReference.Content.Add(
-                                new DocumentReference.ContentComponent()
-                                {
-                                    Attachment = attachment,
-                                    Format = code,
-                                });
-                            resourceUpdated = true;
-                        }
+                        documentReference.Content.Add(
+                            new DocumentReference.ContentComponent()
+                            {
+                                Attachment = attachment,
+                                Format = code,
+                            });
+                        resourceUpdated = true;
                     }
                 }
 #else
EOF
@@ -728,20 +728,18 @@
#if R4 || R4B || Stu3
foreach (var code in GetClinicalReferenceCodes(diagnosticReport))
{
foreach (var attachment in diagnosticReport.PresentedForm)
foreach (var attachment in diagnosticReport.PresentedForm
.Where(attachment => !documentReference.Content.Any(
x => ClinicalReferenceDuplicatorHelper.CompareCoding(x.Format, code)
&& ClinicalReferenceDuplicatorHelper.CompareAttachment(x.Attachment, attachment))))
{
if (!documentReference.Content.Any(
x => ClinicalReferenceDuplicatorHelper.CompareCoding(x.Format, code)
&& ClinicalReferenceDuplicatorHelper.CompareAttachment(x.Attachment, attachment)))
{
documentReference.Content.Add(
new DocumentReference.ContentComponent()
{
Attachment = attachment,
Format = code,
});
resourceUpdated = true;
}
documentReference.Content.Add(
new DocumentReference.ContentComponent()
{
Attachment = attachment,
Format = code,
});
resourceUpdated = true;
}
}
#else
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +772 to +783
foreach (var content in contents)
{
if (!ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles))
{
content.Profile = content.Profile
.UnionBy(
profiles,
x => (x.Value is Coding) ? (((Coding)x.Value).System, ((Coding)x.Value).Code) : (string.Empty, string.Empty))
.ToList();
resourceUpdated = true;
}
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Where Note

This foreach loop
implicitly filters its target sequence
- consider filtering the sequence explicitly using '.Where(...)'.

Copilot Autofix

AI 2 months ago

The best fix is to filter contents with .Where(...) so the loop iterates only over those content objects where !ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles) holds. Specifically, refactor the foreach to:

foreach (var content in contents.Where(content => !ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles)))
{
    // body as before, without the if statement
}

Then, remove the inner if (now redundant), and dedent the loop body. This reduces the nesting level and clarifies that only content not matching the codings will be processed. No new imports or helpers are required; System.Linq is already imported.

Only the block spanning lines 772–782 in file src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs needs to be changed.


Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.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/Guidance/ClinicalReferenceDuplicator.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicator.cs
@@ -769,17 +769,14 @@
                         continue;
                     }
 
-                    foreach (var content in contents)
+                    foreach (var content in contents.Where(content => !ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles)))
                     {
-                        if (!ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles))
-                        {
-                            content.Profile = content.Profile
-                                .UnionBy(
-                                    profiles,
-                                    x => (x.Value is Coding) ? (((Coding)x.Value).System, ((Coding)x.Value).Code) : (string.Empty, string.Empty))
-                                .ToList();
-                            resourceUpdated = true;
-                        }
+                        content.Profile = content.Profile
+                            .UnionBy(
+                                profiles,
+                                x => (x.Value is Coding) ? (((Coding)x.Value).System, ((Coding)x.Value).Code) : (string.Empty, string.Empty))
+                            .ToList();
+                        resourceUpdated = true;
                     }
                 }
 #endif
EOF
@@ -769,17 +769,14 @@
continue;
}

foreach (var content in contents)
foreach (var content in contents.Where(content => !ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles)))
{
if (!ClinicalReferenceDuplicatorHelper.CompareCodings(content.Profile, profiles))
{
content.Profile = content.Profile
.UnionBy(
profiles,
x => (x.Value is Coding) ? (((Coding)x.Value).System, ((Coding)x.Value).Code) : (string.Empty, string.Empty))
.ToList();
resourceUpdated = true;
}
content.Profile = content.Profile
.UnionBy(
profiles,
x => (x.Value is Coding) ? (((Coding)x.Value).System, ((Coding)x.Value).Code) : (string.Empty, string.Empty))
.ToList();
resourceUpdated = true;
}
}
#endif
Copilot is powered by AI and may make mistakes. Always verify output.
}

return CompareCodings(
x.Where(xx => xx.Value?.GetType() == typeof(Coding)).Select(xx => (Coding)xx.Value).ToList(),

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

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

return CompareCodings(
x.Where(xx => xx.Value?.GetType() == typeof(Coding)).Select(xx => (Coding)xx.Value).ToList(),
y.Where(yy => yy.Value?.GetType() == typeof(Coding)).Select(yy => (Coding)yy.Value).ToList());

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

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

Copilot Autofix

AI 2 months ago

To ensure the code does not dereference a possibly-null variable, we should add an explicit check returning true if both x and y are null, and false if only one is null. This pattern matches the one used in other comparison methods throughout the file (e.g., CompareAttachment and CompareContent). Specifically, insert a block after the method signature to return true if both are null, and false if either is null. Then, the rest of the function is only used when both lists are definitely non-null. All changes belong in src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs within the CompareCodings(IReadOnlyList<DocumentReference.ProfileComponent> x, IReadOnlyList<DocumentReference.ProfileComponent> y) method body.

Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.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/Guidance/ClinicalReferenceDuplicatorHelper.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs
@@ -66,8 +66,13 @@
 #if !R4 && !R4B && !Stu3
         public static bool CompareCodings(IReadOnlyList<DocumentReference.ProfileComponent> x, IReadOnlyList<DocumentReference.ProfileComponent> y)
         {
-            if ((x?.Count ?? 0) != (y?.Count ?? 0))
+            if (x == null || y == null)
             {
+                return x == null && y == null;
+            }
+
+            if (x.Count != y.Count)
+            {
                 return false;
             }
 
EOF
@@ -66,8 +66,13 @@
#if !R4 && !R4B && !Stu3
public static bool CompareCodings(IReadOnlyList<DocumentReference.ProfileComponent> x, IReadOnlyList<DocumentReference.ProfileComponent> y)
{
if ((x?.Count ?? 0) != (y?.Count ?? 0))
if (x == null || y == null)
{
return x == null && y == null;
}

if (x.Count != y.Count)
{
return false;
}

Copilot is powered by AI and may make mistakes. Always verify output.
return false;
}

return x.All(xx => y.Any(yy => CompareContent(xx, yy)));

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

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

Copilot Autofix

AI 2 months ago

To safely handle the possibility of x and/or y being null, we should explicitly check for null before calling methods on these variables. Specifically:

  • Before using x.All() or y.Any(), we should check if either variable is null.
  • Since the logic already checks for count mismatches at line 96, if both are null, this check passes and we end up dereferencing x on line 101.
  • The best way to preserve functionality and avoid changing method semantics is to add an explicit check: if both x and y are null, they are considered equal and the function should return true. If one is null and the other is not, return false. If neither is null, proceed with the original logic.
  • This fix should replace the region of the code starting from the null-check and covering the method logic in CompareContents (lines 95-102). No new imports or dependencies are needed.
Suggested changeset 1
src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.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/Guidance/ClinicalReferenceDuplicatorHelper.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs
--- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Guidance/ClinicalReferenceDuplicatorHelper.cs
@@ -93,8 +93,14 @@
 
         public static bool CompareContents(IReadOnlyList<DocumentReference.ContentComponent> x, IReadOnlyList<DocumentReference.ContentComponent> y)
         {
-            if ((x?.Count ?? 0) != (y?.Count ?? 0))
+            // If both are null, consider them equal; if only one is null, consider them unequal.
+            if (x == null || y == null)
             {
+                return x == null && y == null;
+            }
+
+            if (x.Count != y.Count)
+            {
                 return false;
             }
 
EOF
@@ -93,8 +93,14 @@

public static bool CompareContents(IReadOnlyList<DocumentReference.ContentComponent> x, IReadOnlyList<DocumentReference.ContentComponent> y)
{
if ((x?.Count ?? 0) != (y?.Count ?? 0))
// If both are null, consider them equal; if only one is null, consider them unequal.
if (x == null || y == null)
{
return x == null && y == null;
}

if (x.Count != y.Count)
{
return false;
}

Copilot is powered by AI and may make mistakes. Always verify output.
public async Task GivenResource_WhenDeleting_ThenDuplicateResourceShouldBeDeleted(
Resource resource)
{
(var source, var duplicate) = await CreateResourceAsync(resource);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning test

This assignment to
duplicate
is useless, since its value is never read.

Copilot Autofix

AI 2 months ago

To fix the problem, simply discard the unused variable assignment by replacing var duplicate with _ in the deconstruction. This makes it clear to readers and maintainers of the code that the second value returned by CreateResourceAsync(resource) is intentionally ignored since only source is needed. This should be changed only in the relevant location in the method GivenResource_WhenDeleting_ThenDuplicateResourceShouldBeDeleted. No other method, import or definition changes are needed.

Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.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/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs
@@ -53,7 +53,7 @@
         public async Task GivenResource_WhenDeleting_ThenDuplicateResourceShouldBeDeleted(
             Resource resource)
         {
-            (var source, var duplicate) = await CreateResourceAsync(resource);
+            (var source, _) = await CreateResourceAsync(resource);
             await DeleteResourceAsync(source);
         }
 
EOF
@@ -53,7 +53,7 @@
public async Task GivenResource_WhenDeleting_ThenDuplicateResourceShouldBeDeleted(
Resource resource)
{
(var source, var duplicate) = await CreateResourceAsync(resource);
(var source, _) = await CreateResourceAsync(resource);
await DeleteResourceAsync(source);
}

Copilot is powered by AI and may make mistakes. Always verify output.
string subject,
List<Attachment> attachments)
{
(var source, var duplicate) = await CreateResourceAsync(resource);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning test

This assignment to
duplicate
is useless, since its value is never read.

Copilot Autofix

AI 2 months ago

To fix the problem, replace the assignment to var duplicate with a discard assignment (_). This means changing the code from (var source, var duplicate) to (var source, _) on line 67 inside the method GivenResource_WhenUpdating_ThenDuplicateResourceShouldBeUpdated. This will clarify that the second item of the returned tuple is intentionally not used, and will prevent any future confusion about unused variables. No imports or additional definitions are needed for this fix, as discards are standard in C# for ignoring unused tuple elements.


Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.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/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Guidance/ClinicalReferenceDuplicatorTests.cs
@@ -64,7 +64,7 @@
             string subject,
             List<Attachment> attachments)
         {
-            (var source, var duplicate) = await CreateResourceAsync(resource);
+            (var source, _) = await CreateResourceAsync(resource);
             await UpdateResourceAsync(source, subject, attachments);
         }
 
EOF
@@ -64,7 +64,7 @@
string subject,
List<Attachment> attachments)
{
(var source, var duplicate) = await CreateResourceAsync(resource);
(var source, _) = await CreateResourceAsync(resource);
await UpdateResourceAsync(source, subject, attachments);
}

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 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.

1 participant