Skip to content

Conversation

@brendankowitz
Copy link
Member

Description

This pull request migrates the project from using the MediatR library to the Medino library for in-process messaging and notifications. All usages, references, and documentation have been updated accordingly. Additionally, the minimum version of the shared healthcare package has been updated. The most significant changes are summarized below.

Dependency and Package Management:

  • Replaced the MediatR NuGet package with Medino in all project and props files, and updated the shared healthcare package version from 10.0.68 to 11.0.3. (Directory.Packages.props [1] [2]; src/Microsoft.Health.Fhir.Api/Microsoft.Health.Fhir.Api.csproj [3]

  • Updated third-party notices to reflect the switch from MediatR 9.0.0 to Medino 3.0.2. (THIRDPARTYNOTICES.md THIRDPARTYNOTICES.mdL542-R543)

Codebase Migration to Medino:

  • Replaced all using MediatR; statements with using Medino; across the codebase and tests. (src/Microsoft.Health.Fhir.Api/Features/ApiNotifications/ApiNotificationMiddleware.cs [1] src/Microsoft.Health.Fhir.Api/Features/BackgroundJobService/HostingBackgroundService.cs [2] src/Microsoft.Health.Fhir.Api/Features/ExceptionNotifications/ExceptionNotificationMiddleware.cs [3] src/Microsoft.Health.Fhir.Api/Features/Health/ImproperBehaviorHealthCheck.cs [4] src/Microsoft.Health.Fhir.Api/Features/Health/StorageInitializedHealthCheck.cs [5] src/Microsoft.Health.Fhir.Core.UnitTests/Features/Operations/BulkDelete/BulkDeleteProcessingJobTests.cs [6] src/Microsoft.Health.Fhir.Core.UnitTests/Features/Operations/BulkUpdate/BulkUpdateProcessingJobTests.cs [7] src/Microsoft.Health.Fhir.Core.UnitTests/Features/Operations/Export/CancelExportRequestHandlerTests.cs [8]

  • Updated all code and tests to use PublishAsync instead of Publish for publishing notifications with the Medino mediator. (src/Microsoft.Health.Fhir.Api/Features/ApiNotifications/ApiNotificationMiddleware.cs [1] src/Microsoft.Health.Fhir.Api/Features/ExceptionNotifications/ExceptionNotificationMiddleware.cs [2] src/Microsoft.Health.Fhir.Core.UnitTests/Features/Operations/BulkUpdate/BulkUpdateProcessingJobTests.cs [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]

Documentation and Comments:

  • Updated all class and method documentation to reference Medino instead of MediatR. (src/Microsoft.Health.Fhir.Api/Features/ApiNotifications/ApiResponseNotification.cs [1] src/Microsoft.Health.Fhir.Api/Features/ExceptionNotifications/ExceptionNotification.cs [2]

Related issues

Addresses [issue #].

Testing

Describe how this change was tested.

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)

@brendankowitz brendankowitz force-pushed the personal/bkowitz/update-healthcare-shared branch 8 times, most recently from ae54710 to e60562d Compare November 18, 2025 00:32
}

resources.AddRange(patients.Reverse());
resources.AddRange(((IEnumerable<Patient>)patients).Reverse());

Check warning

Code scanning / CodeQL

Useless upcast Warning test

There is no need to upcast from
Patient\[\]
to
IEnumerable
- the conversion can be done implicitly.

Copilot Autofix

AI 9 days ago

In general, the fix for a "useless upcast" is to remove the explicit cast and let the compiler apply the implicit conversion. This keeps the code simpler without affecting functionality.

Here, on line 492, we have resources.AddRange(((IEnumerable<Patient>)patients).Reverse());. The explicit cast to IEnumerable<Patient> is unnecessary because patients is a Patient[], which already implements IEnumerable<Patient>, and Reverse() is an extension method on IEnumerable<T>. The best minimal fix is to remove the cast and call Reverse() directly on patients, relying on type inference and implicit conversion. No additional imports or helper methods are needed, and existing behavior (adding the reversed patient sequence to resources) remains identical.

Concretely:

  • In test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs, locate the resources.AddRange(((IEnumerable<Patient>)patients).Reverse()); line.
  • Replace it with resources.AddRange(patients.Reverse());.
  • No other code or imports need to be changed.
Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.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/Search/SortTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs
@@ -489,7 +489,7 @@
                 observations.Add(obs.First());
             }
 
-            resources.AddRange(((IEnumerable<Patient>)patients).Reverse());
+            resources.AddRange(patients.Reverse());
             resources.AddRange(observations);
 
             // Ask to get all patient with specific tag order by birthdate (timestamp)
EOF
@@ -489,7 +489,7 @@
observations.Add(obs.First());
}

resources.AddRange(((IEnumerable<Patient>)patients).Reverse());
resources.AddRange(patients.Reverse());
resources.AddRange(observations);

// Ask to get all patient with specific tag order by birthdate (timestamp)
Copilot is powered by AI and may make mistakes. Always verify output.
}

resources.AddRange(patients.Reverse());
resources.AddRange(((IEnumerable<Patient>)patients).Reverse());

Check warning

Code scanning / CodeQL

Useless upcast Warning test

There is no need to upcast from
Patient\[\]
to
IEnumerable
- the conversion can be done implicitly.

Copilot Autofix

AI 9 days ago

To fix a useless upcast, remove the explicit cast and rely on the existing implicit conversion. Here, patients is a Patient[], which already implements IEnumerable<Patient>. List<Resource>.AddRange takes IEnumerable<Resource>, and because Patient derives from Resource, the array can be used directly without any cast.

The specific change is in test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs at line 564. Replace:

resources.AddRange(((IEnumerable<Patient>)patients).Reverse());

with:

resources.AddRange(patients.Reverse());

No additional methods, imports, or definitions are needed. The behavior remains the same: patients is enumerated in reverse order and each Patient (a Resource) is added to the resources list.

Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.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/Search/SortTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/SortTests.cs
@@ -561,7 +561,7 @@
                 observations.Add(obs.First());
             }
 
-            resources.AddRange(((IEnumerable<Patient>)patients).Reverse());
+            resources.AddRange(patients.Reverse());
             observations.Reverse();
             resources.AddRange(observations);
 
EOF
@@ -561,7 +561,7 @@
observations.Add(obs.First());
}

resources.AddRange(((IEnumerable<Patient>)patients).Reverse());
resources.AddRange(patients.Reverse());
observations.Reverse();
resources.AddRange(observations);

Copilot is powered by AI and may make mistakes. Always verify output.

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, null, formatName: formatName);
CreateExportResponse response = await _createExportRequestHandler.Handle(request, _cancellationToken);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning test

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

Copilot Autofix

AI about 2 months ago

To fix this issue, we should remove the useless assignment to the local variable response. We want to preserve the await of the asynchronous method to ensure program logic and timing remains unchanged. Thus, instead of assigning to a variable, we should simply await the operation on its own. All remaining code is unchanged, since none relies on the value assigned to response. Edits are needed for both of the affected methods in the file: remove the assignment and just await the method call.

Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.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.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
@@ -386,7 +386,7 @@
                 Arg.Any<CancellationToken>());
 
             var request = new CreateExportRequest(RequestUrl, ExportJobType.All, null, formatName: formatName);
-            CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
+            await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
 
             Assert.Equal(expectedFormat, actualRecord.ExportFormat);
         }
@@ -407,7 +407,7 @@
                 Arg.Any<CancellationToken>());
 
             var request = new CreateExportRequest(RequestUrl, ExportJobType.All, containerName: containerSpecified ? "test" : null);
-            CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
+            await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
 
             Assert.Equal(expectedFormat, actualRecord.ExportFormat);
         }
EOF
@@ -386,7 +386,7 @@
Arg.Any<CancellationToken>());

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, null, formatName: formatName);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Assert.Equal(expectedFormat, actualRecord.ExportFormat);
}
@@ -407,7 +407,7 @@
Arg.Any<CancellationToken>());

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, containerName: containerSpecified ? "test" : null);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Assert.Equal(expectedFormat, actualRecord.ExportFormat);
}
Copilot is powered by AI and may make mistakes. Always verify output.

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, containerName: containerSpecified ? "test" : null);
CreateExportResponse response = await _createExportRequestHandler.Handle(request, _cancellationToken);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning test

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

Copilot Autofix

AI about 2 months ago

The best fix is very simple: remove the assignment of the result of _createExportRequestHandler.HandleAsync to the unused local variable response. Instead, simply await the method directly (without capturing its result), preserving proper sequencing and exception propagation, but eliminating unnecessary unused assignment. Only the assignment line itself (CreateExportResponse response = await ...) needs to be replaced with an awaited call without the variable and type declaration. No additional imports, methods, or definitions are needed.

Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.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.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
@@ -407,7 +407,7 @@
                 Arg.Any<CancellationToken>());
 
             var request = new CreateExportRequest(RequestUrl, ExportJobType.All, containerName: containerSpecified ? "test" : null);
-            CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
+            await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
 
             Assert.Equal(expectedFormat, actualRecord.ExportFormat);
         }
EOF
@@ -407,7 +407,7 @@
Arg.Any<CancellationToken>());

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, containerName: containerSpecified ? "test" : null);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Assert.Equal(expectedFormat, actualRecord.ExportFormat);
}
Copilot is powered by AI and may make mistakes. Always verify output.

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, filters: filters);
CreateExportResponse response = await _createExportRequestHandler.Handle(request, _cancellationToken);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning test

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

Copilot Autofix

AI about 2 months ago

To fix the problem, you should remove the assignment to the variable response while retaining the awaited call to _createExportRequestHandler.HandleAsync(...), in order to preserve any expected side effects or exceptions that may result from calling it. Specifically, in file test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs, in method GivenARequestWithFilters_WhenConverted_ThenTheFiltersArePopulated, replace:

CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

with:

await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

No additional imports, methods, or definitions are required.


Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.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.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Export/CreateExportRequestHandlerTests.cs
@@ -434,7 +434,7 @@
                 Arg.Any<CancellationToken>());
 
             var request = new CreateExportRequest(RequestUrl, ExportJobType.All, filters: filters);
-            CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
+            await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
 
             Assert.Collection(
                 actualRecord.Filters,
EOF
@@ -434,7 +434,7 @@
Arg.Any<CancellationToken>());

var request = new CreateExportRequest(RequestUrl, ExportJobType.All, filters: filters);
CreateExportResponse response = await _createExportRequestHandler.HandleAsync(request, _cancellationToken);
await _createExportRequestHandler.HandleAsync(request, _cancellationToken);

Assert.Collection(
actualRecord.Filters,
Copilot is powered by AI and may make mistakes. Always verify output.
}

CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None);
CreateReindexResponse response = await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning test

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

Copilot Autofix

AI 9 days ago

To fix the problem, remove the unnecessary assignment to the local variable while still invoking _createReindexRequestHandler.HandleAsync(request, CancellationToken.None) so the test continues to exercise the code path and potentially throw exceptions. Since the test only cares that an exception is thrown (and the associated errorMessage), there is no need to capture the return value at all.

Concretely, in test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Reindex/ReindexJobTests.cs, within the GivenOutOfRangeReindexParameter_WhenCreatingAReindexJob_ThenExceptionShouldBeThrown method, replace the line:

CreateReindexResponse response = await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);

with a statement that simply awaits the task:

await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);

No additional imports, methods, or definitions are required, and existing functionality is preserved because the method is still executed and any thrown exceptions are still caught and asserted.

Suggested changeset 1
test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Reindex/ReindexJobTests.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.Integration/Features/Operations/Reindex/ReindexJobTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Reindex/ReindexJobTests.cs
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Reindex/ReindexJobTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Features/Operations/Reindex/ReindexJobTests.cs
@@ -416,7 +416,7 @@
                         break;
                 }
 
-                CreateReindexResponse response = await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);
+                await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);
             }
             catch (FhirException fhirExp)
             {
EOF
@@ -416,7 +416,7 @@
break;
}

CreateReindexResponse response = await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);
await _createReindexRequestHandler.HandleAsync(request, CancellationToken.None);
}
catch (FhirException fhirExp)
{
Copilot is powered by AI and may make mistakes. Always verify output.
@brendankowitz brendankowitz force-pushed the personal/bkowitz/update-healthcare-shared branch 7 times, most recently from 7c41da5 to dc2ac20 Compare November 27, 2025 00:48
@brendankowitz
Copy link
Member Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@brendankowitz brendankowitz force-pushed the personal/bkowitz/update-healthcare-shared branch 2 times, most recently from f1158bb to 37981ca Compare December 8, 2025 23:55
@brendankowitz brendankowitz force-pushed the personal/bkowitz/update-healthcare-shared branch from 37981ca to cddf0ba Compare December 16, 2025 22:57
@brendankowitz brendankowitz force-pushed the personal/bkowitz/update-healthcare-shared branch from 34f2691 to 79a897e Compare December 29, 2025 21:01
brendankowitz and others added 7 commits December 29, 2025 13:25
- Fix DeleteSearchParameterBehavior.cs to use Medino instead of MediatR
- Update test method calls from Handle to HandleAsync for Medino compatibility
- Add ConfigureAwait(false) to async enumeration in ResourceManagerCollectionSetup

These changes resolve build failures that were preventing CosmosDB deployments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
- Update test calls from Handle to HandleAsync
- Handle method is now protected, tests must use public HandleAsync

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@brendankowitz brendankowitz force-pushed the personal/bkowitz/update-healthcare-shared branch from 79a897e to 0e5a915 Compare December 29, 2025 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants