diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 29caf63..0b8cc44 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -164,3 +164,49 @@ jobs: poetry publish --build --no-interaction -r pypi $version = poetry version Write-Host "Published ``$version`` to Pypi." >> $Env:GITHUB_STEP_SUMMARY + + csharp: + name: CSharp + permissions: + id-token: write + contents: read + packages: write + needs: + - test + - version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup .NET + uses: actions/setup-dotnet@v5.2.0 # v5.2.0 + with: + dotnet-version: | + 8.0.x + 10.0.x + - name: Restore dotnet tools + working-directory: dotnet + run: dotnet tool restore + - name: Check formatting + working-directory: dotnet + run: dotnet csharpier check . + - name: Restore dependencies + working-directory: dotnet + run: dotnet restore OrchestrateSDK.DotNet.sln + - name: Pack + working-directory: dotnet + run: dotnet pack src/CareEvolution.Orchestrate/CareEvolution.Orchestrate.csproj --configuration Release --no-restore -p:Version=${{ needs.version.outputs.version }} -o ./artifacts + - name: Upload package artifact + if: needs.version.outputs.target == 'dev' + uses: actions/upload-artifact@v4 + with: + name: csharp-nuget-package-${{ needs.version.outputs.version }} + path: dotnet/artifacts/*.nupkg + - name: Publish NuGet + if: needs.version.outputs.target == 'prod' + working-directory: dotnet + run: | + dotnet nuget push ./artifacts/*.nupkg \ + --source https://proget.careevolution.com/nuget/nuget/ \ + --api-key ${{ secrets.PROGET_TOKEN }} \ + --skip-duplicate + echo "Published `${{ needs.version.outputs.version }}` to NuGet.org." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92719c8..23fc316 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Test +permissions: + contents: read + on: push: branches: @@ -117,3 +120,34 @@ jobs: - name: Test working-directory: python run: poetry run pytest -m "${{ (inputs.suite || 'default') }}" + + csharp: + name: CSharp + runs-on: ubuntu-latest + needs: + - python + strategy: + fail-fast: true + matrix: + dotnet-version: ["8.0.x", "10.0.x"] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Setup .NET + uses: actions/setup-dotnet@v5.2.0 # v5.2.0 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore dotnet tools + working-directory: dotnet + run: dotnet tool restore + - name: Check formatting + working-directory: dotnet + run: dotnet csharpier check . + - name: Restore dependencies + working-directory: dotnet + run: dotnet restore OrchestrateSDK.DotNet.sln + - name: Build + working-directory: dotnet + run: dotnet build OrchestrateSDK.DotNet.sln --configuration Release --no-restore + - name: Test + working-directory: dotnet + run: dotnet test OrchestrateSDK.DotNet.sln --configuration Release --no-build diff --git a/.gitignore b/.gitignore index 00a600b..9e44016 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ **/.mypy_cache/ **/.pytest_cache/ **/.ipynb_checkpoints/ -**/__pycache__/ \ No newline at end of file +**/__pycache__/ +.codex diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7e8822..1704926 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,15 @@ # Orchestrate SDK Contribution Guide -The Orchestrate SDK is a TypeScript and JavaScript library for interacting with the Orchestrate API at . Releases are tagged and generated from `main`. Development should be forked from `main` and a PR created to merge back into it. +The Orchestrate SDK is a TypeScript, Python, and C# library for interacting with the Orchestrate API at . Releases are tagged and generated from `main`. Development should be forked from `main` and a PR created to merge back into it. ## Installation -For TypeScript, navigate to the `./typescript` directory and install dependencies with `npm i`. For Python, navigate to the `./python` directory and install dependencies with `poetry install`. +For TypeScript, navigate to the `./typescript` directory and install dependencies with `npm i`. +For Python, navigate to the `./python` directory and install dependencies with `poetry install`. +For C#, navigate to the `./dotnet` directory and restore dependencies with `dotnet restore`. ## Tests -TypeScript tests can be run and watched with `npm run test:watch`. Python tests can be run with `poetry run pytest`. +TypeScript tests can be run and watched with `npm run test:watch`. Python tests can be run with `poetry run pytest`. C# tests can be run with `dotnet test`. To run Local Hashing Service tests, docker must be installed and running. Tests against the Identity API do not use data directly from the Local Hashing Service, so any valid hash key can be used to start the container. See the [Orchestrate Docs](https://orchestrate.docs.careevolution.com/identity/local_hash/hosting.html) for information on starting the container. diff --git a/README.md b/README.md index dd711ca..e77a31f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Orchestrate SDK -The Orchestrate SDK is a TypeScript and JavaScript library for interacting with the Orchestrate API at . +The Orchestrate SDK provides TypeScript, Python, and C# clients for interacting with the Orchestrate API at . Full documentation of the API is available at . @@ -18,9 +18,15 @@ Python: pip install orchestrate-api ``` +C#: + +```bash +dotnet add package CareEvolution.Orchestrate +``` + ## Usage -TypeScript: +### TypeScript ```typescript import { OrchestrateApi } from '@careevolution/orchestrate'; @@ -32,7 +38,7 @@ await orchestrate.terminology.classifyCondition({ }); ``` -Python: +### Python ```python from orchestrate import OrchestrateApi @@ -41,11 +47,38 @@ api = OrchestrateApi(api_key="your-api-key") api.terminology.classify_condition(code="119981000146107", system="SNOMED") ``` +### C\# + +```csharp +using CareEvolution.Orchestrate; + +var api = new OrchestrateApi(new OrchestrateClientOptions +{ + ApiKey = "your-api-key", +}); + +await api.Terminology.ClassifyConditionAsync(new ClassifyConditionRequest +{ + Code = "119981000146107", + System = "SNOMED", +}); +``` + +Additionally, C# also supports dependency injection with `IOrchestrateApi` and `OrchestrateApi` registered in the service collection. + +```csharp +using CareEvolution.Orchestrate; +using Microsoft.Extensions.DependencyInjection; + +var services = new ServiceCollection(); +services.AddOrchestrateApi(); +``` + ## Configuration The SDK supports environment variables for configuring HTTP behavior. These can be used for local development, CI, or shared runtime configuration. -For the primary `OrchestrateApi` clients in both TypeScript and Python: +For the primary `OrchestrateApi` clients in TypeScript, Python, and C#: | Environment variable | Purpose | Default | | --- | --- | --- | @@ -60,7 +93,7 @@ Environment variables used by the identity clients: | --- | --- | | `ORCHESTRATE_IDENTITY_URL` | Base URL for `IdentityApi`. Required unless the URL is passed directly when creating the client. | | `ORCHESTRATE_IDENTITY_API_KEY` | API key sent as the `x-api-key` header for `IdentityApi`. | -| `ORCHESTRATE_IDENTITY_METRICS_KEY` | Metrics key sent as the `Authorization` header for `IdentityApi`. A value with or without the `Basic ` prefix is accepted. | +| `ORCHESTRATE_IDENTITY_METRICS_KEY` | Metrics key sent as the `Authorization` header for `IdentityApi`. A value with or without the `Basic` prefix is accepted. | | `ORCHESTRATE_IDENTITY_LOCAL_HASHING_URL` | Base URL for `LocalHashingApi`. Required unless the URL is passed directly when creating the client. | ### Configuration Precedence @@ -71,13 +104,13 @@ When the same setting is provided in more than one place, the SDK resolves it in 2. The matching environment variable 3. The SDK default, when one exists -For example, passing `api_key` or `timeout_ms` in Python, or `apiKey` or `timeoutMs` in TypeScript, overrides the corresponding environment variable. +For example, passing `api_key` or `timeout_ms` in Python, `apiKey` or `timeoutMs` in TypeScript, or `ApiKey` or `TimeoutMs` in C# overrides the corresponding environment variable. `ORCHESTRATE_ADDITIONAL_HEADERS` is additive. It is merged into the request headers before the SDK applies its standard `Accept`, `Content-Type`, authentication, and metrics headers, so the SDK-managed headers take precedence if the same header name is supplied in multiple places. ### Examples -TypeScript: +#### TypeScript Example ```bash export ORCHESTRATE_API_KEY="your-api-key" @@ -91,7 +124,7 @@ import { OrchestrateApi } from '@careevolution/orchestrate'; const orchestrate = new OrchestrateApi(); ``` -Python: +#### Python Example ```bash export ORCHESTRATE_API_KEY="your-api-key" @@ -104,3 +137,19 @@ from orchestrate import OrchestrateApi api = OrchestrateApi() ``` + +#### C\# Example + +With environment values as above or DI configuration: + +```csharp +using Microsoft.Extensions.DependencyInjection; +using CareEvolution.Orchestrate; + +var services = new ServiceCollection(); +services.AddOrchestrateApi(options => +{ + options.ApiKey = "your-api-key"; + options.TimeoutMs = 30000; +}); +``` diff --git a/dotnet/.config/dotnet-tools.json b/dotnet/.config/dotnet-tools.json new file mode 100644 index 0000000..8d74243 --- /dev/null +++ b/dotnet/.config/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "csharpier": { + "version": "1.2.6", + "commands": [ + "csharpier" + ], + "rollForward": false + } + } +} diff --git a/dotnet/.csharpierignore b/dotnet/.csharpierignore new file mode 100644 index 0000000..db7fbb2 --- /dev/null +++ b/dotnet/.csharpierignore @@ -0,0 +1,3 @@ +**/bin +**/obj +tests/CareEvolution.Orchestrate.Tests/LiveData/** diff --git a/dotnet/.gitignore b/dotnet/.gitignore new file mode 100644 index 0000000..ae84b25 --- /dev/null +++ b/dotnet/.gitignore @@ -0,0 +1,3 @@ +**/bin/ +**/obj/ +TestResults/ diff --git a/dotnet/OrchestrateSDK.DotNet.sln b/dotnet/OrchestrateSDK.DotNet.sln new file mode 100644 index 0000000..260f374 --- /dev/null +++ b/dotnet/OrchestrateSDK.DotNet.sln @@ -0,0 +1,27 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CareEvolution.Orchestrate", "src/CareEvolution.Orchestrate/CareEvolution.Orchestrate.csproj", "{B21EA726-0E99-4CDB-8F56-7A3300565A0B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CareEvolution.Orchestrate.Tests", "tests/CareEvolution.Orchestrate.Tests/CareEvolution.Orchestrate.Tests.csproj", "{3259B89E-A60A-432D-A4B5-547646506A24}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B21EA726-0E99-4CDB-8F56-7A3300565A0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B21EA726-0E99-4CDB-8F56-7A3300565A0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B21EA726-0E99-4CDB-8F56-7A3300565A0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B21EA726-0E99-4CDB-8F56-7A3300565A0B}.Release|Any CPU.Build.0 = Release|Any CPU + {3259B89E-A60A-432D-A4B5-547646506A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3259B89E-A60A-432D-A4B5-547646506A24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3259B89E-A60A-432D-A4B5-547646506A24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3259B89E-A60A-432D-A4B5-547646506A24}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/dotnet/src/CareEvolution.Orchestrate/AssemblyInfo.cs b/dotnet/src/CareEvolution.Orchestrate/AssemblyInfo.cs new file mode 100644 index 0000000..ca57be2 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("CareEvolution.Orchestrate.Tests")] diff --git a/dotnet/src/CareEvolution.Orchestrate/BatchRequest.cs b/dotnet/src/CareEvolution.Orchestrate/BatchRequest.cs new file mode 100644 index 0000000..f469bb3 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/BatchRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +internal sealed class BatchRequest +{ + public required IReadOnlyList Items { get; init; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/BatchResponse.cs b/dotnet/src/CareEvolution.Orchestrate/BatchResponse.cs new file mode 100644 index 0000000..cc3c0b5 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/BatchResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +internal sealed class BatchResponse +{ + public required List Items { get; init; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/CareEvolution.Orchestrate.csproj b/dotnet/src/CareEvolution.Orchestrate/CareEvolution.Orchestrate.csproj new file mode 100644 index 0000000..3575f2b --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/CareEvolution.Orchestrate.csproj @@ -0,0 +1,26 @@ + + + net8.0;net10.0 + enable + enable + latest + true + $(NoWarn);1591 + CareEvolution.Orchestrate + CareEvolution Orchestrate SDK + SDK for the Orchestrate API at api.careevolutionapi.com + https://rosetta-api.docs.careevolution.com/ + https://github.com/CareEvolution/OrchestrateSDK + Apache-2.0 + CareEvolution + 0.0.0 + + + + + + + diff --git a/dotnet/src/CareEvolution.Orchestrate/ClassifyConditionRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ClassifyConditionRequest.cs new file mode 100644 index 0000000..4ebb636 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ClassifyConditionRequest.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ClassifyConditionRequest +{ + public required string Code { get; set; } + + public required string System { get; set; } + + public string? Display { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ClassifyConditionResponse.cs b/dotnet/src/CareEvolution.Orchestrate/ClassifyConditionResponse.cs new file mode 100644 index 0000000..333ff91 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ClassifyConditionResponse.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace CareEvolution.Orchestrate; + +public sealed class ClassifyConditionResponse +{ + [JsonPropertyName("ccsrCatgory")] + public CodeableConcept? CcsrCategory { get; set; } + + public Coding? CcsrDefaultInpatient { get; set; } + + public Coding? CcsrDefaultOutpatient { get; set; } + + public bool CciChronic { get; set; } + + public bool CciAcute { get; set; } + + public CodeableConcept? HccCategory { get; set; } + + public bool Behavioral { get; set; } + + public bool Substance { get; set; } + + public bool SocialDeterminant { get; set; } + + public string? Covid19Condition { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ClassifyMedicationRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ClassifyMedicationRequest.cs new file mode 100644 index 0000000..9486848 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ClassifyMedicationRequest.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ClassifyMedicationRequest +{ + public required string Code { get; set; } + + public required string System { get; set; } + + public string? Display { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ClassifyMedicationResponse.cs b/dotnet/src/CareEvolution.Orchestrate/ClassifyMedicationResponse.cs new file mode 100644 index 0000000..f109aac --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ClassifyMedicationResponse.cs @@ -0,0 +1,14 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ClassifyMedicationResponse +{ + public List MedRtTherapeuticClass { get; set; } = []; + + public List RxNormIngredient { get; set; } = []; + + public string? RxNormStrength { get; set; } + + public bool RxNormGeneric { get; set; } + + public string? Covid19Rx { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ClassifyObservationRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ClassifyObservationRequest.cs new file mode 100644 index 0000000..9db9878 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ClassifyObservationRequest.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ClassifyObservationRequest +{ + public required string Code { get; set; } + + public required string System { get; set; } + + public string? Display { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ClassifyObservationResponse.cs b/dotnet/src/CareEvolution.Orchestrate/ClassifyObservationResponse.cs new file mode 100644 index 0000000..aaaf10a --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ClassifyObservationResponse.cs @@ -0,0 +1,18 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ClassifyObservationResponse +{ + public string? LoincComponent { get; set; } + + public string? LoincClass { get; set; } + + public string? LoincSystem { get; set; } + + public string? LoincMethodType { get; set; } + + public string? LoincTimeAspect { get; set; } + + public string? Covid19Lab { get; set; } + + public string? Category { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertApi.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertApi.cs new file mode 100644 index 0000000..5b6b484 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertApi.cs @@ -0,0 +1,349 @@ +using System.Net.Http; + +namespace CareEvolution.Orchestrate; + +public interface IConvertApi +{ + Task CdaToFhirR4Async( + ConvertCdaToFhirR4Request request, + CancellationToken cancellationToken = default + ); + Task CdaToHtmlAsync( + ConvertCdaToHtmlRequest request, + CancellationToken cancellationToken = default + ); + Task CdaToPdfAsync( + ConvertCdaToPdfRequest request, + CancellationToken cancellationToken = default + ); + Task CombineFhirR4BundlesAsync( + ConvertCombineFhirR4BundlesRequest request, + CancellationToken cancellationToken = default + ); + Task FhirDstu2ToFhirR4Async( + ConvertFhirDstu2ToFhirR4Request request, + CancellationToken cancellationToken = default + ); + Task FhirR4ToCdaAsync( + ConvertFhirR4ToCdaRequest request, + CancellationToken cancellationToken = default + ); + Task FhirR4ToHealthLakeAsync( + ConvertFhirR4ToHealthLakeRequest request, + CancellationToken cancellationToken = default + ); + Task FhirR4ToManifestAsync( + ConvertFhirR4ToManifestRequest request, + CancellationToken cancellationToken = default + ); + Task FhirR4ToNemsisV34Async( + ConvertFhirR4ToNemsisV34Request request, + CancellationToken cancellationToken = default + ); + Task FhirR4ToNemsisV35Async( + ConvertFhirR4ToNemsisV35Request request, + CancellationToken cancellationToken = default + ); + Task FhirR4ToOmopAsync( + ConvertFhirR4ToOmopRequest request, + CancellationToken cancellationToken = default + ); + Task FhirStu3ToFhirR4Async( + ConvertFhirStu3ToFhirR4Request request, + CancellationToken cancellationToken = default + ); + Task Hl7ToFhirR4Async( + ConvertHl7ToFhirR4Request request, + CancellationToken cancellationToken = default + ); + Task X12ToFhirR4Async( + ConvertX12ToFhirR4Request request, + CancellationToken cancellationToken = default + ); +} + +public sealed class ConvertApi : IConvertApi +{ + private readonly OrchestrateHttpClient _http; + + internal ConvertApi(OrchestrateHttpClient http) + { + _http = http; + } + + public Task Hl7ToFhirR4Async( + ConvertHl7ToFhirR4Request request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/convert/v1/hl7tofhirr4", + [ + new KeyValuePair("patientId", request.PatientId), + new KeyValuePair("patientIdentifier", request.PatientIdentifier), + new KeyValuePair( + "patientIdentifierSystem", + request.PatientIdentifierSystem + ), + new KeyValuePair("tz", request.Tz), + new KeyValuePair("processingHint", request.ProcessingHint), + ] + ); + return PostTextForJsonAsync( + route, + request.Content, + "text/plain", + cancellationToken + ); + } + + public Task CdaToFhirR4Async( + ConvertCdaToFhirR4Request request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/convert/v1/cdatofhirr4", + [ + new KeyValuePair("patientId", request.PatientId), + new KeyValuePair("patientIdentifier", request.PatientIdentifier), + new KeyValuePair( + "patientIdentifierSystem", + request.PatientIdentifierSystem + ), + new KeyValuePair( + "includeOriginalCda", + request.IncludeOriginalCda?.ToString()?.ToLowerInvariant() + ), + new KeyValuePair( + "includeStandardizedCda", + request.IncludeStandardizedCda?.ToString()?.ToLowerInvariant() + ), + ] + ); + return PostTextForJsonAsync( + route, + request.Content, + "application/xml", + cancellationToken + ); + } + + public Task CdaToPdfAsync( + ConvertCdaToPdfRequest request, + CancellationToken cancellationToken = default + ) => + PostTextAsync( + "/convert/v1/cdatopdf", + request.Content, + "application/xml", + "application/pdf", + ResponseKind.Bytes, + cancellationToken + ); + + public Task FhirR4ToCdaAsync( + ConvertFhirR4ToCdaRequest request, + CancellationToken cancellationToken = default + ) => + PostJsonAsync( + "/convert/v1/fhirr4tocda", + request.Content, + "application/xml", + ResponseKind.Text, + cancellationToken + ); + + public Task FhirR4ToOmopAsync( + ConvertFhirR4ToOmopRequest request, + CancellationToken cancellationToken = default + ) => + PostJsonAsync( + "/convert/v1/fhirr4toomop", + request.Content, + "application/zip", + ResponseKind.Bytes, + cancellationToken + ); + + public Task CombineFhirR4BundlesAsync( + ConvertCombineFhirR4BundlesRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/convert/v1/combinefhirr4bundles", + [ + new KeyValuePair("patientId", request.PatientId), + new KeyValuePair("patientIdentifier", request.PatientIdentifier), + new KeyValuePair( + "patientIdentifierSystem", + request.PatientIdentifierSystem + ), + ] + ); + return PostTextForJsonAsync( + route, + request.Content, + "application/x-ndjson", + cancellationToken + ); + } + + public Task X12ToFhirR4Async( + ConvertX12ToFhirR4Request request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/convert/v1/x12tofhirr4", + [ + new KeyValuePair("patientId", request.PatientId), + new KeyValuePair("patientIdentifier", request.PatientIdentifier), + new KeyValuePair( + "patientIdentifierSystem", + request.PatientIdentifierSystem + ), + ] + ); + return PostTextForJsonAsync( + route, + request.Content, + "text/plain", + cancellationToken + ); + } + + public Task FhirDstu2ToFhirR4Async( + ConvertFhirDstu2ToFhirR4Request request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/convert/v1/fhirdstu2tofhirr4", + request.Content, + cancellationToken + ); + + public Task FhirStu3ToFhirR4Async( + ConvertFhirStu3ToFhirR4Request request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/convert/v1/fhirstu3tofhirr4", + request.Content, + cancellationToken + ); + + public Task FhirR4ToHealthLakeAsync( + ConvertFhirR4ToHealthLakeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/convert/v1/fhirr4tohealthlake", + request.Content, + cancellationToken + ); + + public Task CdaToHtmlAsync( + ConvertCdaToHtmlRequest request, + CancellationToken cancellationToken = default + ) => + PostTextAsync( + "/convert/v1/cdatohtml", + request.Content, + "application/xml", + "text/html", + ResponseKind.Text, + cancellationToken + ); + + public Task FhirR4ToNemsisV34Async( + ConvertFhirR4ToNemsisV34Request request, + CancellationToken cancellationToken = default + ) => + PostJsonAsync( + "/convert/v1/fhirr4tonemsisv34", + request.Content, + "application/xml", + ResponseKind.Text, + cancellationToken + ); + + public Task FhirR4ToNemsisV35Async( + ConvertFhirR4ToNemsisV35Request request, + CancellationToken cancellationToken = default + ) => + PostJsonAsync( + "/convert/v1/fhirr4tonemsisv35", + request.Content, + "application/xml", + ResponseKind.Text, + cancellationToken + ); + + public Task FhirR4ToManifestAsync( + ConvertFhirR4ToManifestRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/convert/v1/fhirr4tomanifest", + [ + new KeyValuePair("delimiter", request.Delimiter), + new KeyValuePair("source", request.Source), + new KeyValuePair("patientIdentifier", request.PatientIdentifier), + new KeyValuePair("setting", request.Setting), + ] + ); + return PostJsonAsync( + route, + request.Content, + "application/zip", + ResponseKind.Bytes, + cancellationToken + ); + } + + private Task PostTextForJsonAsync( + string route, + string content, + string contentType, + CancellationToken cancellationToken + ) => + PostTextAsync( + route, + content, + contentType, + "application/json", + ResponseKind.Json, + cancellationToken + ); + + private Task PostTextAsync( + string route, + string content, + string contentType, + string accept, + ResponseKind responseKind, + CancellationToken cancellationToken + ) + { + var httpContent = new StringContent(content); + httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue( + contentType + ); + return _http.PostAsync(route, httpContent, accept, responseKind, cancellationToken); + } + + private Task PostJsonAsync( + string route, + Bundle content, + string accept, + ResponseKind responseKind, + CancellationToken cancellationToken + ) + { + var httpContent = OrchestrateHttpClient.CreateJsonContent(content); + return _http.PostAsync(route, httpContent, accept, responseKind, cancellationToken); + } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToFhirR4Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToFhirR4Request.cs new file mode 100644 index 0000000..445b2e9 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToFhirR4Request.cs @@ -0,0 +1,16 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertCdaToFhirR4Request +{ + public required string Content { get; set; } + + public string? PatientId { get; set; } + + public string? PatientIdentifier { get; set; } + + public string? PatientIdentifierSystem { get; set; } + + public bool? IncludeOriginalCda { get; set; } + + public bool? IncludeStandardizedCda { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToHtmlRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToHtmlRequest.cs new file mode 100644 index 0000000..5a65ece --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToHtmlRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertCdaToHtmlRequest +{ + public required string Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToPdfRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToPdfRequest.cs new file mode 100644 index 0000000..353f841 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertCdaToPdfRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertCdaToPdfRequest +{ + public required string Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertCombineFhirR4BundlesRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertCombineFhirR4BundlesRequest.cs new file mode 100644 index 0000000..699bd46 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertCombineFhirR4BundlesRequest.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertCombineFhirR4BundlesRequest +{ + public required string Content { get; set; } + + public string? PatientId { get; set; } + + public string? PatientIdentifier { get; set; } + + public string? PatientIdentifierSystem { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirDstu2ToFhirR4Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirDstu2ToFhirR4Request.cs new file mode 100644 index 0000000..487d8f9 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirDstu2ToFhirR4Request.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirDstu2ToFhirR4Request +{ + public required object Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToCdaRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToCdaRequest.cs new file mode 100644 index 0000000..6651f87 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToCdaRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirR4ToCdaRequest +{ + public required Bundle Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToHealthLakeRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToHealthLakeRequest.cs new file mode 100644 index 0000000..66d24f0 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToHealthLakeRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirR4ToHealthLakeRequest +{ + public required Bundle Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToManifestRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToManifestRequest.cs new file mode 100644 index 0000000..c8a2ac4 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToManifestRequest.cs @@ -0,0 +1,14 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirR4ToManifestRequest +{ + public required Bundle Content { get; set; } + + public string? Delimiter { get; set; } + + public string? Source { get; set; } + + public string? PatientIdentifier { get; set; } + + public string? Setting { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToNemsisV34Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToNemsisV34Request.cs new file mode 100644 index 0000000..e7d8db7 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToNemsisV34Request.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirR4ToNemsisV34Request +{ + public required Bundle Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToNemsisV35Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToNemsisV35Request.cs new file mode 100644 index 0000000..cbeca66 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToNemsisV35Request.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirR4ToNemsisV35Request +{ + public required Bundle Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToOmopRequest.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToOmopRequest.cs new file mode 100644 index 0000000..62ab2f7 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirR4ToOmopRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirR4ToOmopRequest +{ + public required Bundle Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertFhirStu3ToFhirR4Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirStu3ToFhirR4Request.cs new file mode 100644 index 0000000..6f6b3b0 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertFhirStu3ToFhirR4Request.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertFhirStu3ToFhirR4Request +{ + public required object Content { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertHl7ToFhirR4Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertHl7ToFhirR4Request.cs new file mode 100644 index 0000000..a5bfe74 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertHl7ToFhirR4Request.cs @@ -0,0 +1,16 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertHl7ToFhirR4Request +{ + public required string Content { get; set; } + + public string? PatientId { get; set; } + + public string? PatientIdentifier { get; set; } + + public string? PatientIdentifierSystem { get; set; } + + public string? Tz { get; set; } + + public string? ProcessingHint { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertRequestFactory.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertRequestFactory.cs new file mode 100644 index 0000000..9e6acad --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertRequestFactory.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace CareEvolution.Orchestrate; + +public static class ConvertRequestFactory +{ + public static ConvertCombineFhirR4BundlesRequest GenerateConvertCombinedFhirBundlesRequestFromBundles( + IEnumerable fhirBundles, + string? personId = null + ) + { + var content = string.Join("\n", fhirBundles.Select(OrchestrateHttpClient.Serialize)); + + return new ConvertCombineFhirR4BundlesRequest { Content = content, PatientId = personId }; + } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ConvertX12ToFhirR4Request.cs b/dotnet/src/CareEvolution.Orchestrate/ConvertX12ToFhirR4Request.cs new file mode 100644 index 0000000..badcba5 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ConvertX12ToFhirR4Request.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate; + +public sealed class ConvertX12ToFhirR4Request +{ + public required string Content { get; set; } + + public string? PatientId { get; set; } + + public string? PatientIdentifier { get; set; } + + public string? PatientIdentifierSystem { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/EnvironmentConfiguration.cs b/dotnet/src/CareEvolution.Orchestrate/EnvironmentConfiguration.cs new file mode 100644 index 0000000..03b91bf --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/EnvironmentConfiguration.cs @@ -0,0 +1,178 @@ +using System.Text.Json; + +namespace CareEvolution.Orchestrate; + +internal static class EnvironmentConfiguration +{ + private const string DefaultBaseUrl = "https://api.careevolutionapi.com"; + private const int DefaultTimeoutMs = 120_000; + private const string BaseUrlEnvironmentVariable = "ORCHESTRATE_BASE_URL"; + private const string IdentityUrlEnvironmentVariable = "ORCHESTRATE_IDENTITY_URL"; + private const string LocalHashingUrlEnvironmentVariable = + "ORCHESTRATE_IDENTITY_LOCAL_HASHING_URL"; + private const string AdditionalHeadersEnvironmentVariable = "ORCHESTRATE_ADDITIONAL_HEADERS"; + private const string ApiKeyEnvironmentVariable = "ORCHESTRATE_API_KEY"; + private const string IdentityApiKeyEnvironmentVariable = "ORCHESTRATE_IDENTITY_API_KEY"; + private const string IdentityMetricsKeyEnvironmentVariable = "ORCHESTRATE_IDENTITY_METRICS_KEY"; + private const string TimeoutEnvironmentVariable = "ORCHESTRATE_TIMEOUT_MS"; + + public static ResolvedConfiguration Resolve(OrchestrateClientOptions? options) + { + var additionalHeaders = GetAdditionalHeaders(); + var configuration = new ResolvedConfiguration( + BaseUrl: GetPriorityOrMissing(options?.BaseUrl, BaseUrlEnvironmentVariable) + ?? DefaultBaseUrl, + TimeoutMs: GetTimeout(options?.TimeoutMs), + AdditionalHeaders: additionalHeaders, + ApiKey: GetPriority(options?.ApiKey, ApiKeyEnvironmentVariable) + ); + ValidateExactlyOneAuthenticationHeader(configuration); + return configuration; + } + + public static ResolvedConfiguration Resolve(IdentityApiOptions? options) + { + var url = GetPriority(options?.Url, IdentityUrlEnvironmentVariable); + if (string.IsNullOrWhiteSpace(url)) + { + throw new ArgumentException( + $"Identity URL is required. Specify in the constructor or set '{IdentityUrlEnvironmentVariable}' environment variable." + ); + } + + var metricsKey = GetPriority(options?.MetricsKey, IdentityMetricsKeyEnvironmentVariable); + var additionalHeaders = GetAdditionalHeaders(); + var configuration = new ResolvedConfiguration( + BaseUrl: url, + TimeoutMs: GetTimeout(options?.TimeoutMs), + AdditionalHeaders: additionalHeaders, + ApiKey: GetPriority(options?.ApiKey, IdentityApiKeyEnvironmentVariable), + Authorization: string.IsNullOrWhiteSpace(metricsKey) + ? null + : $"Basic {NormalizeBasicCredential(metricsKey)}" + ); + ValidateExactlyOneAuthenticationHeader(configuration); + return configuration; + } + + public static ResolvedConfiguration Resolve(LocalHashingApiOptions? options) + { + var url = GetPriority(options?.Url, LocalHashingUrlEnvironmentVariable); + if (string.IsNullOrWhiteSpace(url)) + { + throw new ArgumentException( + $"Local hashing URL is required. Specify in the constructor or set '{LocalHashingUrlEnvironmentVariable}' environment variable." + ); + } + + return new ResolvedConfiguration( + BaseUrl: url, + TimeoutMs: GetTimeout(options?.TimeoutMs), + AdditionalHeaders: GetAdditionalHeaders() + ); + } + + private static string? GetPriority(string? explicitValue, string environmentVariable) + { + return explicitValue ?? Environment.GetEnvironmentVariable(environmentVariable); + } + + private static string? GetPriorityOrMissing(string? explicitValue, string environmentVariable) + { + if (!string.IsNullOrWhiteSpace(explicitValue)) + { + return explicitValue; + } + + var environmentValue = Environment.GetEnvironmentVariable(environmentVariable); + return string.IsNullOrWhiteSpace(environmentValue) ? null : environmentValue; + } + + private static int GetTimeout(int? explicitTimeoutMs) + { + var rawValue = + explicitTimeoutMs?.ToString() + ?? Environment.GetEnvironmentVariable(TimeoutEnvironmentVariable); + if (rawValue is null) + { + return DefaultTimeoutMs; + } + + if (!int.TryParse(rawValue, out var timeoutMs)) + { + throw new ArgumentException( + $"Invalid timeout value in environment variable '{TimeoutEnvironmentVariable}': {rawValue}" + ); + } + + return timeoutMs; + } + + private static IReadOnlyDictionary GetAdditionalHeaders() + { + var rawValue = Environment.GetEnvironmentVariable(AdditionalHeadersEnvironmentVariable); + if (string.IsNullOrWhiteSpace(rawValue)) + { + return new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + using var document = JsonDocument.Parse(rawValue); + if (document.RootElement.ValueKind != JsonValueKind.Object) + { + throw new ArgumentException( + $"Environment variable '{AdditionalHeadersEnvironmentVariable}' must be a JSON object." + ); + } + + var headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var property in document.RootElement.EnumerateObject()) + { + headers[property.Name] = + property.Value.GetString() + ?? throw new ArgumentException( + $"Environment variable '{AdditionalHeadersEnvironmentVariable}' must map header names to string values." + ); + } + + return headers; + } + + private static string NormalizeBasicCredential(string metricsKey) + { + var normalized = metricsKey.Trim(); + const string prefix = "Basic "; + if ( + normalized.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) + && normalized.Length > prefix.Length + ) + { + normalized = normalized[prefix.Length..].TrimStart(); + } + + return normalized; + } + + private static void ValidateExactlyOneAuthenticationHeader(ResolvedConfiguration configuration) + { + var hasApiKey = + HasConfiguredValue(configuration.ApiKey) + || HasConfiguredHeader(configuration.AdditionalHeaders, "x-api-key"); + var hasAuthorization = + HasConfiguredValue(configuration.Authorization) + || HasConfiguredHeader(configuration.AdditionalHeaders, "Authorization"); + + if (hasApiKey == hasAuthorization) + { + throw new ArgumentException( + "Exactly one authentication header must be configured: either 'x-api-key' or 'Authorization'." + ); + } + } + + private static bool HasConfiguredHeader( + IReadOnlyDictionary headers, + string headerName + ) => headers.TryGetValue(headerName, out var value) && HasConfiguredValue(value); + + private static bool HasConfiguredValue(string? value) => !string.IsNullOrWhiteSpace(value); +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateClientException.cs b/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateClientException.cs new file mode 100644 index 0000000..8fd8897 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateClientException.cs @@ -0,0 +1,25 @@ +using System.Net; +using Hl7.Fhir.Model; + +namespace CareEvolution.Orchestrate.Exceptions; + +/// +/// Raised when the Orchestrate API returns a 4xx or 5xx status code. +/// +public sealed class OrchestrateClientException( + string responseText, + IReadOnlyList issues, + HttpStatusCode statusCode, + OperationOutcome? operationOutcome = null +) + : OrchestrateHttpException( + issues.Count > 0 ? $"\n * {string.Join(" \n * ", issues)}" : responseText, + statusCode + ) +{ + public string ResponseText { get; } = responseText; + + public IReadOnlyList Issues { get; } = issues; + + public OperationOutcome? OperationOutcome { get; } = operationOutcome; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateException.cs b/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateException.cs new file mode 100644 index 0000000..f1c00ff --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateException.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Exceptions; + +/// +/// Base class for all Orchestrate exceptions. +/// +public class OrchestrateException(string message) : Exception(message) { } diff --git a/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateHttpException.cs b/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateHttpException.cs new file mode 100644 index 0000000..ecf2c71 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Exceptions/OrchestrateHttpException.cs @@ -0,0 +1,12 @@ +using System.Net; + +namespace CareEvolution.Orchestrate.Exceptions; + +/// +/// Raised when an HTTP request to the Orchestrate API fails. +/// +public class OrchestrateHttpException(string message, HttpStatusCode? statusCode = null) + : OrchestrateException(message) +{ + public HttpStatusCode? StatusCode { get; } = statusCode; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/GetFhirR4CodeSystemRequest.cs b/dotnet/src/CareEvolution.Orchestrate/GetFhirR4CodeSystemRequest.cs new file mode 100644 index 0000000..1f7ddf7 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/GetFhirR4CodeSystemRequest.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate; + +public sealed class GetFhirR4CodeSystemRequest +{ + public required string CodeSystem { get; set; } + + public int? PageNumber { get; set; } + + public int? PageSize { get; set; } + + public string? ConceptContains { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/GetFhirR4ValueSetRequest.cs b/dotnet/src/CareEvolution.Orchestrate/GetFhirR4ValueSetRequest.cs new file mode 100644 index 0000000..3791ee4 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/GetFhirR4ValueSetRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class GetFhirR4ValueSetRequest +{ + public required string Id { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/GetFhirR4ValueSetsByScopeRequest.cs b/dotnet/src/CareEvolution.Orchestrate/GetFhirR4ValueSetsByScopeRequest.cs new file mode 100644 index 0000000..4e8481f --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/GetFhirR4ValueSetsByScopeRequest.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate; + +public sealed class GetFhirR4ValueSetsByScopeRequest +{ + public string? Name { get; set; } + + public int? PageNumber { get; set; } + + public int? PageSize { get; set; } + + public string? Scope { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/GlobalUsings.cs b/dotnet/src/CareEvolution.Orchestrate/GlobalUsings.cs new file mode 100644 index 0000000..dd27951 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/GlobalUsings.cs @@ -0,0 +1,8 @@ +global using CareEvolution.Orchestrate.Identity; +global using Bundle = Hl7.Fhir.Model.R4.Bundle; +global using CodeableConcept = Hl7.Fhir.Model.CodeableConcept; +global using CodeSystem = Hl7.Fhir.Model.R4.CodeSystem; +global using Coding = Hl7.Fhir.Model.Coding; +global using ConceptMap = Hl7.Fhir.Model.R4.ConceptMap; +global using Parameters = Hl7.Fhir.Model.Parameters; +global using ValueSet = Hl7.Fhir.Model.R4.ValueSet; diff --git a/dotnet/src/CareEvolution.Orchestrate/IOrchestrateHttpClient.cs b/dotnet/src/CareEvolution.Orchestrate/IOrchestrateHttpClient.cs new file mode 100644 index 0000000..9bea51b --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/IOrchestrateHttpClient.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; + +namespace CareEvolution.Orchestrate; + +[EditorBrowsable(EditorBrowsableState.Advanced)] +public interface IOrchestrateHttpClient +{ + HttpClient HttpClient { get; } + + Task SendAsync( + HttpMethod method, + string path, + HttpContent? content = null, + string accept = "application/json", + CancellationToken cancellationToken = default + ); + + Task GetJsonAsync(string path, CancellationToken cancellationToken = default); + + Task PostJsonAsync( + string path, + object? body, + CancellationToken cancellationToken = default + ); +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/AddMatchGuidanceRequest.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/AddMatchGuidanceRequest.cs new file mode 100644 index 0000000..800bbbf --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/AddMatchGuidanceRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class AddMatchGuidanceRequest : MatchGuidanceRequest +{ + public required string Action { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/AddMatchGuidanceResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/AddMatchGuidanceResponse.cs new file mode 100644 index 0000000..4544f22 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/AddMatchGuidanceResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class AddMatchGuidanceResponse +{ + public List ChangedPersons { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateBlindedRecordRequest.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateBlindedRecordRequest.cs new file mode 100644 index 0000000..2186cfc --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateBlindedRecordRequest.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class AddOrUpdateBlindedRecordRequest +{ + public required string Source { get; set; } + + public required string Identifier { get; set; } + + public required BlindedDemographic BlindedDemographic { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateRecordRequest.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateRecordRequest.cs new file mode 100644 index 0000000..663fa68 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateRecordRequest.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class AddOrUpdateRecordRequest +{ + public required string Source { get; set; } + + public required string Identifier { get; set; } + + public required Demographic Demographic { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateRecordResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateRecordResponse.cs new file mode 100644 index 0000000..d85d957 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/AddOrUpdateRecordResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class AddOrUpdateRecordResponse : MatchedPersonReference +{ + public Advisories? Advisories { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/Advisories.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/Advisories.cs new file mode 100644 index 0000000..1cd4902 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/Advisories.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class Advisories +{ + public List InvalidDemographicFields { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/BlindedDemographic.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/BlindedDemographic.cs new file mode 100644 index 0000000..f5b52c2 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/BlindedDemographic.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate.Identity; + +public class BlindedDemographic +{ + public required string Data { get; set; } + + public int Version { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/DatasourceOverlapRecord.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/DatasourceOverlapRecord.cs new file mode 100644 index 0000000..6f759f3 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/DatasourceOverlapRecord.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class DatasourceOverlapRecord +{ + public string? DatasourceA { get; set; } + + public string? DatasourceB { get; set; } + + public int OverlapCount { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/DeleteRecordResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/DeleteRecordResponse.cs new file mode 100644 index 0000000..5e06787 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/DeleteRecordResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class DeleteRecordResponse +{ + public List ChangedPersons { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/Demographic.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/Demographic.cs new file mode 100644 index 0000000..1a6736b --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/Demographic.cs @@ -0,0 +1,43 @@ +using System.Text.Json.Serialization; + +namespace CareEvolution.Orchestrate.Identity; + +public sealed class Demographic +{ + public string? FirstName { get; set; } + + public string? MiddleName { get; set; } + + public string? LastName { get; set; } + + public string? MaidenName { get; set; } + + public string? Gender { get; set; } + + public string? Race { get; set; } + + public string? HomePhoneNumber { get; set; } + + public string? CellPhoneNumber { get; set; } + + public string? Email { get; set; } + + public string? Dob { get; set; } + + public string? Street { get; set; } + + public string? City { get; set; } + + public string? State { get; set; } + + public string? ZipCode { get; set; } + + public string? Mrn { get; set; } + + public string? Hcid { get; set; } + + public string? Ssn { get; set; } + + [JsonPropertyName("medicaidID")] + public string? MedicaidId { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/GetPersonByIdRequest.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/GetPersonByIdRequest.cs new file mode 100644 index 0000000..bfabc0e --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/GetPersonByIdRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class GetPersonByIdRequest +{ + public required string Id { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/HashDemographicResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/HashDemographicResponse.cs new file mode 100644 index 0000000..7771ec1 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/HashDemographicResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class HashDemographicResponse : BlindedDemographic +{ + public Advisories? Advisories { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/IdentifierMetricsRecord.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentifierMetricsRecord.cs new file mode 100644 index 0000000..eafc29c --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentifierMetricsRecord.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class IdentifierMetricsRecord +{ + public string? IdentifierType { get; set; } + + public int RecordCount { get; set; } + + public double RecordRatio { get; set; } + + public string? Source { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/IdentifierMetricsResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentifierMetricsResponse.cs new file mode 100644 index 0000000..ece8d6d --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentifierMetricsResponse.cs @@ -0,0 +1,16 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class IdentifierMetricsResponse +{ + public string? Refreshed { get; set; } + + public int TotalRecordCount { get; set; } + + public int TotalPersonCount { get; set; } + + public List GlobalMetricsRecords { get; set; } = []; + + public List SummaryMetricsRecords { get; set; } = []; + + public List SourceTotals { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/IdentityApi.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentityApi.cs new file mode 100644 index 0000000..4a16bed --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentityApi.cs @@ -0,0 +1,108 @@ +using System.ComponentModel; + +namespace CareEvolution.Orchestrate.Identity; + +public sealed class IdentityApi +{ + private readonly OrchestrateHttpClient _http; + + public IdentityApi(HttpClient httpClient, IdentityApiOptions? options = null) + { + _http = new OrchestrateHttpClient(EnvironmentConfiguration.Resolve(options), httpClient); + Monitoring = new IdentityMonitoringApi(_http); + } + + public IdentityMonitoringApi Monitoring { get; } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IOrchestrateHttpClient Transport => _http; + + public HttpClient HttpClient => _http.HttpClient; + + public Task AddOrUpdateRecordAsync( + AddOrUpdateRecordRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + $"/mpi/v1/record/{BuildSourceIdentifierRoute(request.Source, request.Identifier)}", + request.Demographic, + cancellationToken + ); + + public Task AddOrUpdateBlindedRecordAsync( + AddOrUpdateBlindedRecordRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + $"/mpi/v1/blindedRecord/{BuildSourceIdentifierRoute(request.Source, request.Identifier)}", + request.BlindedDemographic, + cancellationToken + ); + + public Task GetPersonByRecordAsync( + Record request, + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + $"/mpi/v1/record/{BuildSourceIdentifierRoute(request.Source, request.Identifier)}", + cancellationToken + ); + + public Task GetPersonByIdAsync( + GetPersonByIdRequest request, + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + $"/mpi/v1/person/{RouteBuilder.Escape(request.Id)}", + cancellationToken + ); + + public Task MatchDemographicsAsync( + Demographic request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync("/mpi/v1/match", request, cancellationToken); + + public Task MatchBlindedDemographicsAsync( + BlindedDemographic request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/mpi/v1/matchBlinded", + request, + cancellationToken + ); + + public Task DeleteRecordAsync( + Record request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + $"/mpi/v1/deleteRecord/{BuildSourceIdentifierRoute(request.Source, request.Identifier)}", + new { }, + cancellationToken + ); + + public Task AddMatchGuidanceAsync( + AddMatchGuidanceRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/mpi/v1/addGuidance", + request, + cancellationToken + ); + + public Task RemoveMatchGuidanceAsync( + MatchGuidanceRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/mpi/v1/removeGuidance", + request, + cancellationToken + ); + + private static string BuildSourceIdentifierRoute(string source, string identifier) => + $"{RouteBuilder.Escape(source)}/{RouteBuilder.Escape(identifier)}"; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/IdentityMonitoringApi.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentityMonitoringApi.cs new file mode 100644 index 0000000..fba133c --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/IdentityMonitoringApi.cs @@ -0,0 +1,27 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class IdentityMonitoringApi +{ + private readonly OrchestrateHttpClient _http; + + internal IdentityMonitoringApi(OrchestrateHttpClient http) + { + _http = http; + } + + public Task IdentifierMetricsAsync( + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + "/monitoring/v1/identifierMetrics", + cancellationToken + ); + + public Task OverlapMetricsAsync( + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + "/monitoring/v1/overlapMetrics", + cancellationToken + ); +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/LocalHashingApi.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/LocalHashingApi.cs new file mode 100644 index 0000000..ea22b1d --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/LocalHashingApi.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; + +namespace CareEvolution.Orchestrate.Identity; + +public sealed class LocalHashingApi(HttpClient httpClient, LocalHashingApiOptions? options = null) +{ + private readonly OrchestrateHttpClient _http = new( + EnvironmentConfiguration.Resolve(options), + httpClient + ); + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IOrchestrateHttpClient Transport => _http; + + public HttpClient HttpClient => _http.HttpClient; + + public Task HashAsync( + Demographic demographic, + CancellationToken cancellationToken = default + ) => _http.PostJsonAsync("/hash", demographic, cancellationToken); +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/MatchBlindedDemographicsResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchBlindedDemographicsResponse.cs new file mode 100644 index 0000000..9b198b8 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchBlindedDemographicsResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace CareEvolution.Orchestrate.Identity; + +public sealed class MatchBlindedDemographicsResponse +{ + [JsonPropertyName("matchingPersons")] + public List MatchingPersons { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/MatchDemographicsResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchDemographicsResponse.cs new file mode 100644 index 0000000..fbbcb34 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchDemographicsResponse.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace CareEvolution.Orchestrate.Identity; + +public sealed class MatchDemographicsResponse +{ + [JsonPropertyName("matchingPersons")] + public List MatchingPersons { get; set; } = []; + + public List Advisories { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/MatchGuidanceRequest.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchGuidanceRequest.cs new file mode 100644 index 0000000..15d6889 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchGuidanceRequest.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate.Identity; + +public class MatchGuidanceRequest +{ + public required Record RecordOne { get; set; } + + public required Record RecordTwo { get; set; } + + public required string Comment { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/MatchedPersonReference.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchedPersonReference.cs new file mode 100644 index 0000000..2f3f53c --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/MatchedPersonReference.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate.Identity; + +public class MatchedPersonReference +{ + public Person? MatchedPerson { get; set; } + + public List ChangedPersons { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/OverlapMetricsResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/OverlapMetricsResponse.cs new file mode 100644 index 0000000..4da309d --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/OverlapMetricsResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class OverlapMetricsResponse +{ + public List DatasourceOverlapRecords { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/Person.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/Person.cs new file mode 100644 index 0000000..b771e98 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/Person.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class Person +{ + public required string Id { get; set; } + + public List Records { get; set; } = []; + + public int Version { get; set; } + + public PersonStatus? Status { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/PersonStatus.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/PersonStatus.cs new file mode 100644 index 0000000..7b5147b --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/PersonStatus.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class PersonStatus +{ + public string? Code { get; set; } + + public List SupersededBy { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/Record.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/Record.cs new file mode 100644 index 0000000..18f30af --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/Record.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class Record +{ + public required string Source { get; set; } + + public required string Identifier { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/RemoveMatchGuidanceResponse.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/RemoveMatchGuidanceResponse.cs new file mode 100644 index 0000000..e2bab13 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/RemoveMatchGuidanceResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class RemoveMatchGuidanceResponse +{ + public List ChangedPersons { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Identity/SourceTotal.cs b/dotnet/src/CareEvolution.Orchestrate/Identity/SourceTotal.cs new file mode 100644 index 0000000..ff1ade9 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Identity/SourceTotal.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate.Identity; + +public sealed class SourceTotal +{ + public string? Source { get; set; } + + public int TotalRecordCount { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/InsightApi.cs b/dotnet/src/CareEvolution.Orchestrate/InsightApi.cs new file mode 100644 index 0000000..2634596 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/InsightApi.cs @@ -0,0 +1,35 @@ +namespace CareEvolution.Orchestrate; + +public interface IInsightApi +{ + Task RiskProfileAsync( + InsightRiskProfileRequest request, + CancellationToken cancellationToken = default + ); +} + +public sealed class InsightApi : IInsightApi +{ + private readonly OrchestrateHttpClient _http; + + internal InsightApi(OrchestrateHttpClient http) + { + _http = http; + } + + public Task RiskProfileAsync( + InsightRiskProfileRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/insight/v1/riskprofile", + [ + new KeyValuePair("hcc_version", request.HccVersion), + new KeyValuePair("period_end_date", request.PeriodEndDate), + new KeyValuePair("ra_segment", request.RaSegment), + ] + ); + return _http.PostJsonAsync(route, request.Content, cancellationToken); + } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/InsightRiskProfileRequest.cs b/dotnet/src/CareEvolution.Orchestrate/InsightRiskProfileRequest.cs new file mode 100644 index 0000000..523eaa2 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/InsightRiskProfileRequest.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate; + +public sealed class InsightRiskProfileRequest +{ + public required Bundle Content { get; set; } + + public string? HccVersion { get; set; } + + public string? PeriodEndDate { get; set; } + + public string? RaSegment { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Options.cs b/dotnet/src/CareEvolution.Orchestrate/Options.cs new file mode 100644 index 0000000..ebf59c2 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Options.cs @@ -0,0 +1,28 @@ +namespace CareEvolution.Orchestrate; + +public class OrchestrateClientOptions +{ + public string? ApiKey { get; set; } + + public string? BaseUrl { get; set; } + + public int? TimeoutMs { get; set; } +} + +public class IdentityApiOptions +{ + public string? Url { get; set; } + + public string? ApiKey { get; set; } + + public string? MetricsKey { get; set; } + + public int? TimeoutMs { get; set; } +} + +public class LocalHashingApiOptions +{ + public string? Url { get; set; } + + public int? TimeoutMs { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/OrchestrateApi.cs b/dotnet/src/CareEvolution.Orchestrate/OrchestrateApi.cs new file mode 100644 index 0000000..a34c2b4 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/OrchestrateApi.cs @@ -0,0 +1,33 @@ +using System.ComponentModel; + +namespace CareEvolution.Orchestrate; + +public interface IOrchestrateApi +{ + ITerminologyApi Terminology { get; } + IConvertApi Convert { get; } + IInsightApi Insight { get; } + IOrchestrateHttpClient HttpHandler { get; } +} + +public sealed class OrchestrateApi : IOrchestrateApi +{ + private readonly OrchestrateHttpClient _http; + + public OrchestrateApi(HttpClient httpClient, OrchestrateClientOptions? options = null) + { + _http = new OrchestrateHttpClient(EnvironmentConfiguration.Resolve(options), httpClient); + Terminology = new TerminologyApi(_http); + Convert = new ConvertApi(_http); + Insight = new InsightApi(_http); + } + + public ITerminologyApi Terminology { get; } + + public IConvertApi Convert { get; } + + public IInsightApi Insight { get; } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IOrchestrateHttpClient HttpHandler => _http; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/OrchestrateHttpClient.cs b/dotnet/src/CareEvolution.Orchestrate/OrchestrateHttpClient.cs new file mode 100644 index 0000000..6ec14e5 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/OrchestrateHttpClient.cs @@ -0,0 +1,482 @@ +using System.ComponentModel; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using CareEvolution.Orchestrate.Exceptions; +using Hl7.Fhir.Rest; +using Hl7.Fhir.Serialization; + +namespace CareEvolution.Orchestrate; + +internal sealed class OrchestrateHttpClient( + ResolvedConfiguration configuration, + HttpClient httpClient +) : IOrchestrateHttpClient +{ + private static readonly FhirJsonFastParser FhirJsonParser = CreateFhirJsonParser(); + private static readonly FhirJsonFastSerializer FhirJsonSerializer = CreateFhirJsonSerializer(); + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }, + }; + + private readonly HttpClient _httpClient = httpClient; + private readonly ResolvedConfiguration _configuration = configuration; + + public HttpClient HttpClient => _httpClient; + + public async Task GetJsonAsync(string path, CancellationToken cancellationToken = default) + { + var responseText = await SendForStringAsync( + HttpMethod.Get, + path, + content: null, + accept: "application/json", + cancellationToken + ) + .ConfigureAwait(false); + return Deserialize(responseText); + } + + public async Task PostJsonAsync( + string path, + object? body, + CancellationToken cancellationToken = default + ) + { + using var content = CreateJsonContent(body); + var responseText = await SendForStringAsync( + HttpMethod.Post, + path, + content, + accept: "application/json", + cancellationToken + ) + .ConfigureAwait(false); + return Deserialize(responseText); + } + + public async Task PostAsync( + string path, + HttpContent content, + string accept, + ResponseKind responseKind, + CancellationToken cancellationToken = default + ) + { + return responseKind switch + { + ResponseKind.Json => Deserialize( + await SendForStringAsync(HttpMethod.Post, path, content, accept, cancellationToken) + .ConfigureAwait(false) + ), + ResponseKind.Text => (T) + (object) + await SendForStringAsync( + HttpMethod.Post, + path, + content, + accept, + cancellationToken + ) + .ConfigureAwait(false), + ResponseKind.Bytes => (T) + (object) + await SendForBytesAsync( + HttpMethod.Post, + path, + content, + accept, + cancellationToken + ) + .ConfigureAwait(false), + _ => throw new ArgumentOutOfRangeException(nameof(responseKind)), + }; + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public Task SendAsync( + HttpMethod method, + string path, + HttpContent? content = null, + string accept = "application/json", + CancellationToken cancellationToken = default + ) + { + if (typeof(T) == typeof(byte[])) + { + return SendBytesAsync(method, path, content, accept, cancellationToken); + } + + if (typeof(T) == typeof(string)) + { + return SendTextAsync(method, path, content, accept, cancellationToken); + } + + return SendJsonAsync(method, path, content, accept, cancellationToken); + } + + internal static HttpContent CreateJsonContent(object? body) + { + var payload = Serialize(body); + return new StringContent(payload, Encoding.UTF8, "application/json"); + } + + internal static string Serialize(object? body) + { + if (body is null) + { + return "null"; + } + + if (TrySerializeFhir(body) is { } fhirJson) + { + return fhirJson; + } + + return JsonSerializer.Serialize(body, JsonOptions); + } + + private static T Deserialize(string responseText) + { + if (TryDeserializeFhir(responseText) is { } fhirValue) + { + return fhirValue; + } + + return JsonSerializer.Deserialize(responseText, JsonOptions) + ?? throw new InvalidOperationException( + $"Unable to deserialize response as {typeof(T).Name}." + ); + } + + private static string? TrySerializeFhir(object body) + { + if (body is not Hl7.Fhir.Model.Base fhirValue) + { + return null; + } + + return FhirJsonSerializer.SerializeToString(fhirValue, SummaryType.False, null); + } + + private static T? TryDeserializeFhir(string responseText) + { + if ( + !IsFhirType(typeof(T)) + || !responseText.Contains("\"resourceType\"", StringComparison.Ordinal) + ) + { + return default; + } + + return (T?)(object?)FhirJsonParser.Parse(responseText, typeof(T)); + } + + private static bool IsFhirType(Type type) => + type.Namespace?.StartsWith("Hl7.Fhir.Model", StringComparison.Ordinal) == true; + + private static FhirJsonFastParser CreateFhirJsonParser() + { + var settings = new ParserSettings(Hl7.Fhir.Model.Version.R4) + { + AllowUnrecognizedEnums = true, + AcceptUnknownMembers = true, + PermissiveParsing = true, + }; + return new FhirJsonFastParser(settings); + } + + private static FhirJsonFastSerializer CreateFhirJsonSerializer() + { + return new FhirJsonFastSerializer(new SerializerSettings(Hl7.Fhir.Model.Version.R4), false); + } + + private async Task SendForStringAsync( + HttpMethod method, + string path, + HttpContent? content, + string accept, + CancellationToken cancellationToken + ) + { + using var response = await SendAsync(method, path, content, accept, cancellationToken) + .ConfigureAwait(false); + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + + private async Task SendForBytesAsync( + HttpMethod method, + string path, + HttpContent? content, + string accept, + CancellationToken cancellationToken + ) + { + using var response = await SendAsync(method, path, content, accept, cancellationToken) + .ConfigureAwait(false); + return await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); + } + + private async Task SendJsonAsync( + HttpMethod method, + string path, + HttpContent? content, + string accept, + CancellationToken cancellationToken + ) + { + var responseText = await SendForStringAsync( + method, + path, + content, + accept, + cancellationToken + ) + .ConfigureAwait(false); + return Deserialize(responseText); + } + + private async Task SendTextAsync( + HttpMethod method, + string path, + HttpContent? content, + string accept, + CancellationToken cancellationToken + ) => + (T) + (object) + await SendForStringAsync(method, path, content, accept, cancellationToken) + .ConfigureAwait(false); + + private async Task SendBytesAsync( + HttpMethod method, + string path, + HttpContent? content, + string accept, + CancellationToken cancellationToken + ) => + (T) + (object) + await SendForBytesAsync(method, path, content, accept, cancellationToken) + .ConfigureAwait(false); + + private async Task SendAsync( + HttpMethod method, + string path, + HttpContent? content, + string accept, + CancellationToken cancellationToken + ) + { + using var cts = + _configuration.TimeoutMs > 0 + ? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken) + : null; + cts?.CancelAfter(_configuration.TimeoutMs); + var effectiveCancellationToken = cts?.Token ?? cancellationToken; + + using var request = new HttpRequestMessage(method, BuildUri(path)) { Content = content }; + + ApplyHeaders(request.Headers, accept); + + var response = await _httpClient + .SendAsync( + request, + HttpCompletionOption.ResponseHeadersRead, + effectiveCancellationToken + ) + .ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + return response; + } + + throw await CreateExceptionAsync(response, effectiveCancellationToken) + .ConfigureAwait(false); + } + + private Uri BuildUri(string path) + { + var normalizedBaseUrl = _configuration.BaseUrl.TrimEnd('/'); + var normalizedPath = path.StartsWith("/", StringComparison.Ordinal) ? path : $"/{path}"; + return new Uri($"{normalizedBaseUrl}{normalizedPath}", UriKind.Absolute); + } + + private void ApplyHeaders(HttpRequestHeaders headers, string accept) + { + foreach (var header in _configuration.AdditionalHeaders) + { + headers.TryAddWithoutValidation(header.Key, header.Value); + } + + if (!string.IsNullOrWhiteSpace(_configuration.ApiKey)) + { + headers.Remove("x-api-key"); + headers.TryAddWithoutValidation("x-api-key", _configuration.ApiKey); + } + + if (!string.IsNullOrWhiteSpace(_configuration.Authorization)) + { + headers.Remove("Authorization"); + headers.TryAddWithoutValidation("Authorization", _configuration.Authorization); + } + + headers.Accept.Clear(); + headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); + } + + private static async Task CreateExceptionAsync( + HttpResponseMessage response, + CancellationToken cancellationToken + ) + { + var responseText = await response + .Content.ReadAsStringAsync(cancellationToken) + .ConfigureAwait(false); + var operationOutcome = TryParseOperationOutcome(responseText); + var issues = ReadOperationalOutcomes(responseText, operationOutcome); + if ((int)response.StatusCode >= 400 && (int)response.StatusCode < 600) + { + return new OrchestrateClientException( + responseText, + issues, + response.StatusCode, + operationOutcome + ); + } + + return new OrchestrateHttpException(responseText, response.StatusCode); + } + + private static IReadOnlyList ReadOperationalOutcomes( + string responseText, + Hl7.Fhir.Model.OperationOutcome? operationOutcome + ) + { + var fhirOutcomes = ReadFhirOutcomes(operationOutcome); + if (fhirOutcomes.Count > 0) + { + return fhirOutcomes; + } + + var outcomes = ReadJsonOutcomes(responseText); + return outcomes.Count > 0 ? outcomes : [responseText]; + } + + private static Hl7.Fhir.Model.OperationOutcome? TryParseOperationOutcome(string responseText) + { + if (!responseText.Contains("\"resourceType\"", StringComparison.Ordinal)) + { + return null; + } + + try + { + return FhirJsonParser.Parse(responseText); + } + catch + { + return null; + } + } + + private static List ReadFhirOutcomes(Hl7.Fhir.Model.OperationOutcome? operationOutcome) + { + if (operationOutcome?.Issue is null || operationOutcome.Issue.Count == 0) + { + return []; + } + + return operationOutcome + .Issue.Select(issue => + { + var severity = issue.Severity?.ToString()?.ToLowerInvariant() ?? string.Empty; + var code = issue.Code?.ToString()?.ToLowerInvariant() ?? string.Empty; + var diagnostics = issue.Diagnostics ?? string.Empty; + var details = GetIssueDetailString(issue.Details); + var prefix = $"{severity}: {code}"; + var message = string.Join( + "; ", + new[] { details, diagnostics }.Where(static value => + !string.IsNullOrWhiteSpace(value) + ) + ); + return string.IsNullOrWhiteSpace(message) ? prefix : $"{prefix} - {message}"; + }) + .ToList(); + } + + private static List ReadJsonOutcomes(string responseText) + { + try + { + var root = JsonNode.Parse(responseText)?.AsObject(); + if (root is null) + { + return [responseText]; + } + + if ( + root.TryGetPropertyValue("issue", out var issueNode) + && issueNode is JsonArray issuesArray + ) + { + return [responseText]; + } + + if ( + (root["type"]?.GetValue() ?? string.Empty) + == "https://tools.ietf.org/html/rfc9110#section-15.5.1" + ) + { + var title = root["title"]?.GetValue() ?? string.Empty; + var detail = root["detail"]?.GetValue() ?? string.Empty; + return [$"error: {title} - {detail}".TrimEnd()]; + } + } + catch + { + return [responseText]; + } + + return []; + } + + private static string GetIssueDetailString(Hl7.Fhir.Model.CodeableConcept? detail) + { + if (detail is null) + { + return string.Empty; + } + + if (!string.IsNullOrWhiteSpace(detail.Text)) + { + return detail.Text; + } + + if (detail.Coding is not null) + { + foreach (var coding in detail.Coding) + { + var code = coding.Code; + var display = coding.Display; + var joined = string.Join( + ": ", + new[] { code, display }.Where(static value => !string.IsNullOrWhiteSpace(value)) + ); + if (!string.IsNullOrWhiteSpace(joined)) + { + return joined; + } + } + } + + return string.Empty; + } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/Registration.cs b/dotnet/src/CareEvolution.Orchestrate/Registration.cs new file mode 100644 index 0000000..5347303 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/Registration.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace CareEvolution.Orchestrate; + +public static class Registration +{ + public static IServiceCollection AddOrchestrateApi(this IServiceCollection services) => + services.AddOrchestrateApi(configure: null); + + public static IServiceCollection AddOrchestrateApi( + this IServiceCollection services, + Action? configure + ) + { + services.AddOptions(); + + if (configure is not null) + { + services.Configure(configure); + } + + services.AddHttpClient(nameof(OrchestrateApi)); + services.AddTransient(serviceProvider => new OrchestrateApi( + serviceProvider + .GetRequiredService() + .CreateClient(nameof(OrchestrateApi)), + serviceProvider.GetRequiredService>().Value + )); + + return services; + } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/ResolvedConfiguration.cs b/dotnet/src/CareEvolution.Orchestrate/ResolvedConfiguration.cs new file mode 100644 index 0000000..3dd9fb5 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ResolvedConfiguration.cs @@ -0,0 +1,9 @@ +namespace CareEvolution.Orchestrate; + +internal sealed record ResolvedConfiguration( + string BaseUrl, + int TimeoutMs, + IReadOnlyDictionary AdditionalHeaders, + string? ApiKey = null, + string? Authorization = null +); diff --git a/dotnet/src/CareEvolution.Orchestrate/ResponseKind.cs b/dotnet/src/CareEvolution.Orchestrate/ResponseKind.cs new file mode 100644 index 0000000..f90fc50 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/ResponseKind.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate; + +internal enum ResponseKind +{ + Json, + Text, + Bytes, +} diff --git a/dotnet/src/CareEvolution.Orchestrate/RouteBuilder.cs b/dotnet/src/CareEvolution.Orchestrate/RouteBuilder.cs new file mode 100644 index 0000000..75dfddb --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/RouteBuilder.cs @@ -0,0 +1,29 @@ +using Hl7.Fhir.Rest; + +namespace CareEvolution.Orchestrate; + +internal static class RouteBuilder +{ + public static string Build( + string path, + params IEnumerable>[] querySets + ) + { + var query = BuildQuery(querySets.SelectMany(static set => set)); + return string.IsNullOrWhiteSpace(query) ? path : $"{path}?{query}"; + } + + public static string BuildQuery(IEnumerable> values) + { + var segments = values + .Where(static pair => !string.IsNullOrWhiteSpace(pair.Value)) + .Select(static pair => + $"{Uri.EscapeDataString(pair.Key)}={Uri.EscapeDataString(pair.Value!)}" + ) + .ToArray(); + + return string.Join("&", segments); + } + + public static string Escape(string value) => Uri.EscapeDataString(value); +} diff --git a/dotnet/src/CareEvolution.Orchestrate/StandardizeRequest.cs b/dotnet/src/CareEvolution.Orchestrate/StandardizeRequest.cs new file mode 100644 index 0000000..4557de5 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/StandardizeRequest.cs @@ -0,0 +1,12 @@ +namespace CareEvolution.Orchestrate; + +public sealed class StandardizeRequest +{ + public string? Code { get; set; } + + public string? System { get; set; } + + public string? Display { get; set; } + + public string? TargetSystem { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/StandardizeResponse.cs b/dotnet/src/CareEvolution.Orchestrate/StandardizeResponse.cs new file mode 100644 index 0000000..3fb481a --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/StandardizeResponse.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class StandardizeResponse +{ + public List Coding { get; set; } = []; +} diff --git a/dotnet/src/CareEvolution.Orchestrate/StandardizeResponseCoding.cs b/dotnet/src/CareEvolution.Orchestrate/StandardizeResponseCoding.cs new file mode 100644 index 0000000..e318bfa --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/StandardizeResponseCoding.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate; + +public sealed class StandardizeResponseCoding +{ + public string? System { get; set; } + + public string? Code { get; set; } + + public string? Display { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4CodeSystemRequest.cs b/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4CodeSystemRequest.cs new file mode 100644 index 0000000..37c6d16 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4CodeSystemRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class SummarizeFhirR4CodeSystemRequest +{ + public required string CodeSystem { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4ValueSetRequest.cs b/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4ValueSetRequest.cs new file mode 100644 index 0000000..6f8d5ec --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4ValueSetRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class SummarizeFhirR4ValueSetRequest +{ + public required string Id { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4ValueSetScopeRequest.cs b/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4ValueSetScopeRequest.cs new file mode 100644 index 0000000..fb1014f --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/SummarizeFhirR4ValueSetScopeRequest.cs @@ -0,0 +1,6 @@ +namespace CareEvolution.Orchestrate; + +public sealed class SummarizeFhirR4ValueSetScopeRequest +{ + public required string Scope { get; set; } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/TerminologyApi.cs b/dotnet/src/CareEvolution.Orchestrate/TerminologyApi.cs new file mode 100644 index 0000000..93e12a0 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/TerminologyApi.cs @@ -0,0 +1,451 @@ +namespace CareEvolution.Orchestrate; + +public interface ITerminologyApi +{ + Task ClassifyConditionAsync( + ClassifyConditionRequest request, + CancellationToken cancellationToken = default + ); + Task> ClassifyConditionAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task ClassifyMedicationAsync( + ClassifyMedicationRequest request, + CancellationToken cancellationToken = default + ); + Task> ClassifyMedicationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task ClassifyObservationAsync( + ClassifyObservationRequest request, + CancellationToken cancellationToken = default + ); + Task> ClassifyObservationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task GetAllFhirR4ValueSetsForCodesAsync( + Parameters request, + CancellationToken cancellationToken = default + ); + Task GetFhirR4CodeSystemAsync( + GetFhirR4CodeSystemRequest request, + CancellationToken cancellationToken = default + ); + Task GetFhirR4ConceptMapsAsync(CancellationToken cancellationToken = default); + Task GetFhirR4ValueSetAsync( + GetFhirR4ValueSetRequest request, + CancellationToken cancellationToken = default + ); + Task GetFhirR4ValueSetsByScopeAsync( + GetFhirR4ValueSetsByScopeRequest request, + CancellationToken cancellationToken = default + ); + Task GetFhirR4ValueSetScopesAsync(CancellationToken cancellationToken = default); + Task StandardizeBundleAsync( + Bundle bundle, + CancellationToken cancellationToken = default + ); + Task StandardizeConditionAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ); + Task> StandardizeConditionAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task StandardizeLabAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ); + Task> StandardizeLabAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task StandardizeMedicationAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ); + Task> StandardizeMedicationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task StandardizeObservationAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ); + Task> StandardizeObservationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task StandardizeProcedureAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ); + Task> StandardizeProcedureAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task StandardizeRadiologyAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ); + Task> StandardizeRadiologyAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ); + Task SummarizeFhirR4CodeSystemAsync( + SummarizeFhirR4CodeSystemRequest request, + CancellationToken cancellationToken = default + ); + Task SummarizeFhirR4CodeSystemsAsync(CancellationToken cancellationToken = default); + Task SummarizeFhirR4ValueSetAsync( + SummarizeFhirR4ValueSetRequest request, + CancellationToken cancellationToken = default + ); + Task SummarizeFhirR4ValueSetScopeAsync( + SummarizeFhirR4ValueSetScopeRequest request, + CancellationToken cancellationToken = default + ); + Task TranslateFhirR4ConceptMapAsync( + TranslateFhirR4ConceptMapRequest request, + CancellationToken cancellationToken = default + ); +} + +public sealed class TerminologyApi : ITerminologyApi +{ + private readonly OrchestrateHttpClient _http; + + internal TerminologyApi(OrchestrateHttpClient http) + { + _http = http; + } + + public Task ClassifyConditionAsync( + ClassifyConditionRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/classify/condition", + request, + cancellationToken + ); + + public Task> ClassifyConditionAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/classify/condition", + request, + cancellationToken + ); + + public Task ClassifyMedicationAsync( + ClassifyMedicationRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/classify/medication", + request, + cancellationToken + ); + + public Task> ClassifyMedicationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/classify/medication", + request, + cancellationToken + ); + + public Task ClassifyObservationAsync( + ClassifyObservationRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/classify/observation", + request, + cancellationToken + ); + + public Task> ClassifyObservationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/classify/observation", + request, + cancellationToken + ); + + public Task StandardizeConditionAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/condition", + request, + cancellationToken + ); + + public Task> StandardizeConditionAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/standardize/condition", + request, + cancellationToken + ); + + public Task StandardizeMedicationAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/medication", + request, + cancellationToken + ); + + public Task> StandardizeMedicationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/standardize/medication", + request, + cancellationToken + ); + + public Task StandardizeObservationAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/observation", + request, + cancellationToken + ); + + public Task> StandardizeObservationAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/standardize/observation", + request, + cancellationToken + ); + + public Task StandardizeProcedureAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/procedure", + request, + cancellationToken + ); + + public Task> StandardizeProcedureAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/standardize/procedure", + request, + cancellationToken + ); + + public Task StandardizeLabAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/lab", + request, + cancellationToken + ); + + public Task> StandardizeLabAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/standardize/lab", + request, + cancellationToken + ); + + public Task StandardizeRadiologyAsync( + StandardizeRequest request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/radiology", + request, + cancellationToken + ); + + public Task> StandardizeRadiologyAsync( + IReadOnlyList request, + CancellationToken cancellationToken = default + ) => + PostBatchAsync( + "/terminology/v1/standardize/radiology", + request, + cancellationToken + ); + + public Task StandardizeBundleAsync( + Bundle bundle, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/standardize/fhir/r4", + bundle, + cancellationToken + ); + + public Task GetFhirR4CodeSystemAsync( + GetFhirR4CodeSystemRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + $"/terminology/v1/fhir/r4/codesystem/{RouteBuilder.Escape(request.CodeSystem)}", + [ + new KeyValuePair("page.num", request.PageNumber?.ToString()), + new KeyValuePair("_count", request.PageSize?.ToString()), + new KeyValuePair("concept:contains", request.ConceptContains), + ] + ); + return _http.GetJsonAsync(route, cancellationToken); + } + + public Task SummarizeFhirR4CodeSystemsAsync( + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + "/terminology/v1/fhir/r4/codesystem?_summary=true", + cancellationToken + ); + + public Task GetFhirR4ConceptMapsAsync(CancellationToken cancellationToken = default) => + _http.GetJsonAsync("/terminology/v1/fhir/r4/conceptmap", cancellationToken); + + public Task TranslateFhirR4ConceptMapAsync( + TranslateFhirR4ConceptMapRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/terminology/v1/fhir/r4/conceptmap/$translate", + [ + new KeyValuePair("code", request.Code), + new KeyValuePair("domain", request.Domain), + ] + ); + return _http.GetJsonAsync(route, cancellationToken); + } + + public Task SummarizeFhirR4ValueSetScopeAsync( + SummarizeFhirR4ValueSetScopeRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/terminology/v1/fhir/r4/valueset", + [ + new KeyValuePair("extension.scope", request.Scope), + new KeyValuePair("_summary", "true"), + ] + ); + return _http.GetJsonAsync(route, cancellationToken); + } + + public Task SummarizeFhirR4ValueSetAsync( + SummarizeFhirR4ValueSetRequest request, + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + $"/terminology/v1/fhir/r4/valueset/{RouteBuilder.Escape(request.Id)}?_summary=true", + cancellationToken + ); + + public Task GetFhirR4ValueSetAsync( + GetFhirR4ValueSetRequest request, + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + $"/terminology/v1/fhir/r4/valueset/{RouteBuilder.Escape(request.Id)}", + cancellationToken + ); + + public Task GetFhirR4ValueSetsByScopeAsync( + GetFhirR4ValueSetsByScopeRequest request, + CancellationToken cancellationToken = default + ) + { + var route = RouteBuilder.Build( + "/terminology/v1/fhir/r4/valueset", + [ + new KeyValuePair("name", request.Name), + new KeyValuePair("page.num", request.PageNumber?.ToString()), + new KeyValuePair("_count", request.PageSize?.ToString()), + new KeyValuePair("extension.scope", request.Scope), + ] + ); + return _http.GetJsonAsync(route, cancellationToken); + } + + public Task GetFhirR4ValueSetScopesAsync( + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + "/terminology/v1/fhir/r4/valueset/Rosetta.ValueSetScopes", + cancellationToken + ); + + public Task GetAllFhirR4ValueSetsForCodesAsync( + Parameters request, + CancellationToken cancellationToken = default + ) => + _http.PostJsonAsync( + "/terminology/v1/fhir/r4/valueset/$classify", + request, + cancellationToken + ); + + public Task SummarizeFhirR4CodeSystemAsync( + SummarizeFhirR4CodeSystemRequest request, + CancellationToken cancellationToken = default + ) => + _http.GetJsonAsync( + $"/terminology/v1/fhir/r4/codesystem/{RouteBuilder.Escape(request.CodeSystem)}?_summary=true", + cancellationToken + ); + + private async Task> PostBatchAsync( + string route, + IReadOnlyList request, + CancellationToken cancellationToken + ) + { + var response = await _http + .PostJsonAsync>( + $"{route}/batch", + new BatchRequest { Items = request }, + cancellationToken + ) + .ConfigureAwait(false); + return response.Items; + } +} diff --git a/dotnet/src/CareEvolution.Orchestrate/TranslateFhirR4ConceptMapRequest.cs b/dotnet/src/CareEvolution.Orchestrate/TranslateFhirR4ConceptMapRequest.cs new file mode 100644 index 0000000..a598d50 --- /dev/null +++ b/dotnet/src/CareEvolution.Orchestrate/TranslateFhirR4ConceptMapRequest.cs @@ -0,0 +1,8 @@ +namespace CareEvolution.Orchestrate; + +public sealed class TranslateFhirR4ConceptMapRequest +{ + public required string Code { get; set; } + + public string? Domain { get; set; } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/ApiSurfaceTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/ApiSurfaceTests.cs new file mode 100644 index 0000000..e840c99 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/ApiSurfaceTests.cs @@ -0,0 +1,470 @@ +using System.Text.Json; +using CareEvolution.Orchestrate.Tests.Helpers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace CareEvolution.Orchestrate.Tests; + +public sealed class ApiSurfaceTests +{ + private static EnvironmentVariableScope ClearAmbientOrchestrateAuthEnvironment() => + new( + new Dictionary + { + ["ORCHESTRATE_API_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + + [Fact] + public async Task TerminologyBatchShouldPostToBatchRoute() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"items":[{"coding":[]}]}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + var response = await api.Terminology.StandardizeConditionAsync([ + new StandardizeRequest { Code = "123", System = "SNOMED" }, + ]); + + Assert.Single(response); + Assert.NotNull(handler.LastRequest); + Assert.Equal( + "https://api.example.com/terminology/v1/standardize/condition/batch", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + Assert.Contains("\"items\"", handler.LastRequest.Body); + } + + [Fact] + public async Task ConvertHl7ShouldSendPlainTextAndQueryParameters() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("{}")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + await api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request + { + Content = "MSH|^~\\&|", + PatientId = "patient-1", + PatientIdentifier = "identifier-1", + PatientIdentifierSystem = "urn:test", + Tz = "America/New_York", + ProcessingHint = "lab", + } + ); + + Assert.NotNull(handler.LastRequest); + Assert.Equal( + "https://api.example.com/convert/v1/hl7tofhirr4?patientId=patient-1&patientIdentifier=identifier-1&patientIdentifierSystem=urn%3Atest&tz=America%2FNew_York&processingHint=lab", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + Assert.Equal("text/plain", handler.LastRequest.Headers["Content-Type"].Single()); + Assert.Equal("MSH|^~\\&|", handler.LastRequest.Body); + } + + [Fact] + public async Task AdvancedTransportShouldApplyBaseUrlAndDeserializeJson() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"message":"ok"}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + var response = await api.HttpHandler.GetJsonAsync( + "/custom/v1/ping" + ); + + Assert.Equal("ok", response.Message); + Assert.Equal( + "https://api.example.com/custom/v1/ping", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + Assert.Same(httpClient, api.HttpHandler.HttpClient); + } + + [Fact] + public async Task AdvancedTransportShouldSupportTextResponses() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Text("", "text/html")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + using var content = new StringContent(""); + var response = await api.HttpHandler.SendAsync( + HttpMethod.Post, + "/custom/v1/render", + content, + "text/html" + ); + + Assert.Equal("", response); + Assert.Equal("text/html", handler.LastRequest!.Headers["Accept"].Single()); + } + + [Theory] + [InlineData( + "https://api.example.com", + "/custom/v1/ping", + "https://api.example.com/custom/v1/ping" + )] + [InlineData( + "https://api.example.com/", + "custom/v1/ping", + "https://api.example.com/custom/v1/ping" + )] + [InlineData( + "https://api.example.com/", + "/custom/v1/ping", + "https://api.example.com/custom/v1/ping" + )] + public async Task AdvancedTransportShouldNormalizeBaseUrlAndPath( + string baseUrl, + string path, + string expectedUrl + ) + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"message":"ok"}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions { BaseUrl = baseUrl, ApiKey = "test-api-key" } + ); + + _ = await api.HttpHandler.GetJsonAsync(path); + + Assert.Equal(expectedUrl, handler.LastRequest!.RequestUri!.AbsoluteUri); + } + + [Fact] + public void AddOrchestrateApiShouldRegisterIOrchestrateApi() + { + var services = new ServiceCollection(); + + services.AddOrchestrateApi(options => + { + options.BaseUrl = "https://api.example.com"; + options.ApiKey = "test-api-key"; + options.TimeoutMs = 1234; + }); + + using var serviceProvider = services.BuildServiceProvider(); + var api = serviceProvider.GetRequiredService(); + var options = serviceProvider + .GetRequiredService>() + .Value; + + Assert.NotNull(api); + Assert.IsType(api); + Assert.NotNull(api.Terminology); + Assert.NotNull(api.Convert); + Assert.NotNull(api.Insight); + Assert.Equal("https://api.example.com", options.BaseUrl); + Assert.Equal("test-api-key", options.ApiKey); + Assert.Equal(1234, options.TimeoutMs); + } + + [Fact] + public async Task ConvertPdfShouldReturnBytes() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var expected = new byte[] { 1, 2, 3, 4 }; + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Bytes(expected, "application/pdf")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + var response = await api.Convert.CdaToPdfAsync( + new ConvertCdaToPdfRequest { Content = "" } + ); + + Assert.Equal(expected, response); + Assert.Equal("application/pdf", handler.LastRequest!.Headers["Accept"].Single()); + } + + [Fact] + public async Task InsightRiskProfileShouldBuildExpectedQueryString() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("{}")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + await api.Insight.RiskProfileAsync( + new InsightRiskProfileRequest + { + Content = new Bundle(), + HccVersion = "24", + PeriodEndDate = "2026-01-01", + RaSegment = "community nondual aged", + } + ); + + Assert.Equal( + "https://api.example.com/insight/v1/riskprofile?hcc_version=24&period_end_date=2026-01-01&ra_segment=community%20nondual%20aged", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + Assert.Equal( + "application/json; charset=utf-8", + handler.LastRequest.Headers["Content-Type"].Single() + ); + } + + [Fact] + public async Task GetFhirR4CodeSystemShouldEscapePathSegment() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json("""{"resourceType":"CodeSystem","concept":[]}""") + ) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + _ = await api.Terminology.GetFhirR4CodeSystemAsync( + new GetFhirR4CodeSystemRequest { CodeSystem = "SNOMED/CT Demo" } + ); + + Assert.Equal( + "https://api.example.com/terminology/v1/fhir/r4/codesystem/SNOMED%2FCT%20Demo", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + } + + [Fact] + public void CombinedFhirBundleFactoryShouldGenerateNdjson() + { + var request = ConvertRequestFactory.GenerateConvertCombinedFhirBundlesRequestFromBundles( + [new Bundle(), new Bundle()], + "person-1" + ); + + Assert.Equal("person-1", request.PatientId); + Assert.Equal(1, request.Content.Count(character => character == '\n')); + Assert.DoesNotContain("\"ResourceType\":", request.Content, StringComparison.Ordinal); + Assert.Contains("\"resourceType\":\"Bundle\"", request.Content); + } + + [Fact] + public async Task StandardizeBundleShouldSerializeAsFhirJson() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + Assert.Equal(Hl7.Fhir.Model.BundleType.BatchResponse, LiveTestData.R4Bundle.Type); + Assert.NotEmpty(LiveTestData.R4Bundle.Entry); + + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json( + """{"resourceType":"Bundle","type":"collection","entry":[]}""" + ) + ) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + await api.Terminology.StandardizeBundleAsync(LiveTestData.R4Bundle); + + Assert.NotNull(handler.LastRequest); + Assert.Contains("\"resourceType\":\"Bundle\"", handler.LastRequest!.Body); + Assert.DoesNotContain( + "\"ResourceType\":", + handler.LastRequest.Body, + StringComparison.Ordinal + ); + Assert.Contains("\"type\":\"batch-response\"", handler.LastRequest.Body); + } + + [Fact] + public async Task GetAllFhirR4ValueSetsForCodesShouldSerializeParametersWithValueString() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json("""{"resourceType":"Parameters","parameter":[]}""") + ) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + var parameters = new Parameters + { + Parameter = + [ + new Parameters.ParameterComponent + { + Name = "code", + Value = new Hl7.Fhir.Model.FhirString("119981000146107"), + }, + new Parameters.ParameterComponent + { + Name = "system", + Value = new Hl7.Fhir.Model.FhirString("http://snomed.info/sct"), + }, + ], + }; + + await api.Terminology.GetAllFhirR4ValueSetsForCodesAsync(parameters); + + Assert.NotNull(handler.LastRequest); + Assert.Contains("\"resourceType\":\"Parameters\"", handler.LastRequest!.Body); + Assert.Contains("\"name\":\"code\"", handler.LastRequest.Body); + Assert.Contains("\"valueString\":\"119981000146107\"", handler.LastRequest.Body); + Assert.DoesNotContain( + "\"ResourceType\":", + handler.LastRequest.Body, + StringComparison.Ordinal + ); + } + + [Fact] + public async Task ConvertFhirR4ToOmopShouldSerializeAsFhirJson() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Bytes([80, 75, 3, 4], "application/zip")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + _ = await api.Convert.FhirR4ToOmopAsync( + new ConvertFhirR4ToOmopRequest { Content = LiveTestData.R4Bundle } + ); + + Assert.NotNull(handler.LastRequest); + Assert.Contains("\"resourceType\":\"Bundle\"", handler.LastRequest!.Body); + Assert.DoesNotContain( + "\"ResourceType\":", + handler.LastRequest.Body, + StringComparison.Ordinal + ); + Assert.Equal("application/zip", handler.LastRequest.Headers["Accept"].Single()); + } + + [Fact] + public async Task ConvertFhirR4ToNemsisV35ShouldSerializeAsFhirJson() + { + using var environment = ClearAmbientOrchestrateAuthEnvironment(); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Text("", "application/xml")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + _ = await api.Convert.FhirR4ToNemsisV35Async( + new ConvertFhirR4ToNemsisV35Request { Content = LiveTestData.NemsisBundle } + ); + + Assert.NotNull(handler.LastRequest); + Assert.Contains("\"resourceType\":\"Bundle\"", handler.LastRequest!.Body); + Assert.DoesNotContain( + "\"ResourceType\":", + handler.LastRequest.Body, + StringComparison.Ordinal + ); + Assert.Equal("application/xml", handler.LastRequest.Headers["Accept"].Single()); + } + + private sealed class TransportProbeResponse + { + public string? Message { get; set; } + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/AssemblyInfo.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/AssemblyInfo.cs new file mode 100644 index 0000000..2171200 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/CareEvolution.Orchestrate.Tests.csproj b/dotnet/tests/CareEvolution.Orchestrate.Tests/CareEvolution.Orchestrate.Tests.csproj new file mode 100644 index 0000000..ee6474f --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/CareEvolution.Orchestrate.Tests.csproj @@ -0,0 +1,32 @@ + + + net8.0;net10.0 + enable + enable + false + latest + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + PreserveNewest + + + diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/ConfigurationTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/ConfigurationTests.cs new file mode 100644 index 0000000..07bc1a7 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/ConfigurationTests.cs @@ -0,0 +1,325 @@ +using System.Net; +using CareEvolution.Orchestrate.Exceptions; +using CareEvolution.Orchestrate.Tests.Helpers; + +namespace CareEvolution.Orchestrate.Tests; + +public sealed class ConfigurationTests +{ + [Fact] + public async Task OrchestrateApiShouldPreferConstructorValuesOverEnvironmentVariables() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_BASE_URL"] = "https://env.example.com", + ["ORCHESTRATE_API_KEY"] = "env-api-key", + ["ORCHESTRATE_TIMEOUT_MS"] = "45000", + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = + """{"x-custom-header":"custom-value","x-api-key":"wrong"}""", + } + ); + + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"coding":[]}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + ApiKey = "constructor-api-key", + BaseUrl = "https://constructor.example.com", + TimeoutMs = 30000, + } + ); + + await api.Terminology.StandardizeConditionAsync( + new StandardizeRequest { Code = "123", System = "SNOMED" } + ); + + Assert.NotNull(handler.LastRequest); + Assert.Equal( + "https://constructor.example.com/terminology/v1/standardize/condition", + handler.LastRequest!.RequestUri!.ToString() + ); + Assert.Equal("constructor-api-key", handler.LastRequest.Headers["x-api-key"].Single()); + Assert.Equal("custom-value", handler.LastRequest.Headers["x-custom-header"].Single()); + Assert.Equal("application/json", handler.LastRequest.Headers["Accept"].Single()); + } + + [Fact] + public async Task OrchestrateApiShouldTreatWhitespaceBaseUrlAsMissing() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_BASE_URL"] = "https://env.example.com", + ["ORCHESTRATE_API_KEY"] = "env-api-key", + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"coding":[]}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi(httpClient, new OrchestrateClientOptions { BaseUrl = " " }); + + await api.Terminology.StandardizeConditionAsync( + new StandardizeRequest { Code = "123", System = "SNOMED" } + ); + + Assert.Equal( + "https://env.example.com/terminology/v1/standardize/condition", + handler.LastRequest!.RequestUri!.ToString() + ); + } + + [Fact] + public void OrchestrateApiShouldThrowForInvalidTimeoutEnvironmentVariable() + { + using var environment = new EnvironmentVariableScope( + new Dictionary { ["ORCHESTRATE_TIMEOUT_MS"] = "not-a-number" } + ); + + using var httpClient = new HttpClient( + new FakeHttpMessageHandler((_, _) => throw new NotImplementedException()) + ); + var exception = Assert.Throws(() => new OrchestrateApi(httpClient)); + Assert.Contains("ORCHESTRATE_TIMEOUT_MS", exception.Message); + } + + [Fact] + public void OrchestrateApiShouldRequireExactlyOneAuthenticationHeader() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_API_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + + using var httpClient = new HttpClient( + new FakeHttpMessageHandler((_, _) => throw new NotImplementedException()) + ); + var exception = Assert.Throws(() => new OrchestrateApi(httpClient)); + + Assert.Contains("Exactly one authentication header", exception.Message); + } + + [Fact] + public void OrchestrateApiShouldThrowWhenBothAuthenticationHeadersAreConfigured() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_API_KEY"] = "env-api-key", + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = """{"Authorization":"Bearer token"}""", + } + ); + + using var httpClient = new HttpClient( + new FakeHttpMessageHandler((_, _) => throw new NotImplementedException()) + ); + var exception = Assert.Throws(() => new OrchestrateApi(httpClient)); + + Assert.Contains("Exactly one authentication header", exception.Message); + } + + [Fact] + public async Task OrchestrateApiShouldAllowAuthorizationFromAdditionalHeaders() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_API_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = """{"Authorization":"Bearer token"}""", + } + ); + + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"coding":[]}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions { BaseUrl = "https://api.example.com" } + ); + + await api.Terminology.StandardizeConditionAsync( + new StandardizeRequest { Code = "123", System = "SNOMED" } + ); + + Assert.Equal("Bearer token", handler.LastRequest!.Headers["Authorization"].Single()); + Assert.False(handler.LastRequest.Headers.ContainsKey("x-api-key")); + } + + [Fact] + public void IdentityApiShouldRequireUrl() + { + using var environment = new EnvironmentVariableScope( + new Dictionary { ["ORCHESTRATE_IDENTITY_URL"] = null } + ); + + using var httpClient = new HttpClient( + new FakeHttpMessageHandler((_, _) => throw new NotImplementedException()) + ); + var exception = Assert.Throws(() => new IdentityApi(httpClient)); + Assert.Contains("Identity URL is required", exception.Message); + } + + [Theory] + [InlineData("metrics-key", "Basic metrics-key")] + [InlineData("Basic metrics-key", "Basic metrics-key")] + [InlineData("basic metrics-key", "Basic metrics-key")] + [InlineData(" Basic metrics-key ", "Basic metrics-key")] + public async Task IdentityApiShouldNormalizeMetricsKey( + string rawMetricsKey, + string expectedAuthorization + ) + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_URL"] = "https://identity.example.com", + ["ORCHESTRATE_IDENTITY_API_KEY"] = null, + } + ); + + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"datasourceOverlapRecords":[]}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new IdentityApi( + httpClient, + new IdentityApiOptions { MetricsKey = rawMetricsKey } + ); + + await api.Monitoring.OverlapMetricsAsync(); + + Assert.NotNull(handler.LastRequest); + Assert.Equal(expectedAuthorization, handler.LastRequest!.Headers["Authorization"].Single()); + } + + [Fact] + public void IdentityApiShouldRequireExactlyOneAuthenticationHeader() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_URL"] = "https://identity.example.com", + ["ORCHESTRATE_IDENTITY_API_KEY"] = null, + ["ORCHESTRATE_IDENTITY_METRICS_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + + using var httpClient = new HttpClient( + new FakeHttpMessageHandler((_, _) => throw new NotImplementedException()) + ); + var exception = Assert.Throws(() => new IdentityApi(httpClient)); + + Assert.Contains("Exactly one authentication header", exception.Message); + } + + [Fact] + public void IdentityApiShouldThrowWhenBothAuthenticationHeadersAreConfigured() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_URL"] = "https://identity.example.com", + ["ORCHESTRATE_IDENTITY_API_KEY"] = "identity-api-key", + ["ORCHESTRATE_IDENTITY_METRICS_KEY"] = "metrics-key", + } + ); + + using var httpClient = new HttpClient( + new FakeHttpMessageHandler((_, _) => throw new NotImplementedException()) + ); + var exception = Assert.Throws(() => new IdentityApi(httpClient)); + + Assert.Contains("Exactly one authentication header", exception.Message); + } + + [Fact] + public async Task HttpErrorsShouldBeConvertedToOrchestrateClientExceptions() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_API_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json( + """{"resourceType":"OperationOutcome","issue":[{"severity":"error","code":"invalid","diagnostics":"Expected a Bundle but found a Patient"}]}""", + HttpStatusCode.BadRequest + ) + ) + ); + + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + var exception = await Assert.ThrowsAsync(() => + api.Terminology.StandardizeConditionAsync( + new StandardizeRequest { Code = "123", System = "SNOMED" } + ) + ); + + Assert.Contains( + "error: invalid - Expected a Bundle but found a Patient", + exception.Message + ); + Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode); + Assert.NotNull(exception.OperationOutcome); + Assert.Single(exception.OperationOutcome!.Issue); + } + + [Fact] + public async Task MalformedJsonHttpErrorsShouldPreserveRawResponseText() + { + const string responseText = """{"oops":"""; + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Text(responseText, "application/json", HttpStatusCode.BadRequest) + ) + ); + + using var httpClient = new HttpClient(handler); + var api = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions + { + BaseUrl = "https://api.example.com", + ApiKey = "test-api-key", + } + ); + + var exception = await Assert.ThrowsAsync(() => + api.Terminology.StandardizeConditionAsync( + new StandardizeRequest { Code = "123", System = "SNOMED" } + ) + ); + + Assert.Equal(responseText, exception.ResponseText); + Assert.Contains(responseText, exception.Issues); + Assert.Contains(responseText, exception.Message); + Assert.Null(exception.OperationOutcome); + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/GlobalUsings.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/GlobalUsings.cs new file mode 100644 index 0000000..a60cdd4 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/GlobalUsings.cs @@ -0,0 +1,5 @@ +global using CareEvolution.Orchestrate; +global using CareEvolution.Orchestrate.Identity; +global using Xunit; +global using Bundle = Hl7.Fhir.Model.R4.Bundle; +global using Parameters = Hl7.Fhir.Model.Parameters; diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/EnvironmentVariableScope.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/EnvironmentVariableScope.cs new file mode 100644 index 0000000..450b311 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/EnvironmentVariableScope.cs @@ -0,0 +1,23 @@ +namespace CareEvolution.Orchestrate.Tests.Helpers; + +internal sealed class EnvironmentVariableScope : IDisposable +{ + private readonly Dictionary _originalValues = new(StringComparer.Ordinal); + + public EnvironmentVariableScope(IDictionary values) + { + foreach (var pair in values) + { + _originalValues[pair.Key] = Environment.GetEnvironmentVariable(pair.Key); + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + } + + public void Dispose() + { + foreach (var pair in _originalValues) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/FakeHttpMessageHandler.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/FakeHttpMessageHandler.cs new file mode 100644 index 0000000..68ee95b --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/FakeHttpMessageHandler.cs @@ -0,0 +1,107 @@ +using System.Net; +using System.Text; + +namespace CareEvolution.Orchestrate.Tests.Helpers; + +internal sealed class FakeHttpMessageHandler : HttpMessageHandler +{ + private readonly Func< + HttpRequestMessage, + CancellationToken, + Task + > _responder; + + public FakeHttpMessageHandler( + Func> responder + ) + { + _responder = responder; + } + + public RequestSnapshot? LastRequest { get; private set; } + + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken + ) + { + LastRequest = await RequestSnapshot.CreateAsync(request, cancellationToken); + return await _responder(request, cancellationToken); + } +} + +internal sealed record RequestSnapshot( + HttpMethod Method, + Uri? RequestUri, + IReadOnlyDictionary> Headers, + string? Body +) +{ + public static async Task CreateAsync( + HttpRequestMessage request, + CancellationToken cancellationToken + ) + { + var contentHeaders = request.Content is null + ? Enumerable.Empty>>() + : request.Content.Headers; + + var headers = request + .Headers.Concat(contentHeaders) + .ToDictionary( + pair => pair.Key, + pair => (IReadOnlyList)pair.Value.ToList(), + StringComparer.OrdinalIgnoreCase + ); + + var body = request.Content is null + ? null + : await request.Content.ReadAsStringAsync(cancellationToken); + + return new RequestSnapshot(request.Method, request.RequestUri, headers, body); + } +} + +internal static class FakeResponses +{ + public static HttpResponseMessage Json( + string json, + HttpStatusCode statusCode = HttpStatusCode.OK + ) + { + return new HttpResponseMessage(statusCode) + { + Content = new StringContent(json, Encoding.UTF8, "application/json"), + }; + } + + public static HttpResponseMessage Text( + string text, + string mediaType, + HttpStatusCode statusCode = HttpStatusCode.OK + ) + { + return new HttpResponseMessage(statusCode) + { + Content = new StringContent(text, Encoding.UTF8, mediaType), + }; + } + + public static HttpResponseMessage Bytes( + byte[] bytes, + string mediaType, + HttpStatusCode statusCode = HttpStatusCode.OK + ) + { + return new HttpResponseMessage(statusCode) + { + Content = new ByteArrayContent(bytes) + { + Headers = + { + ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mediaType), + }, + }, + }; + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveClients.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveClients.cs new file mode 100644 index 0000000..e816bf8 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveClients.cs @@ -0,0 +1,10 @@ +namespace CareEvolution.Orchestrate.Tests.Helpers; + +internal static class LiveClients +{ + public static OrchestrateApi CreateOrchestrateApi(HttpClient httpClient) => new(httpClient); + + public static IdentityApi CreateIdentityApi(HttpClient httpClient) => new(httpClient); + + public static LocalHashingApi CreateLocalHashingApi(HttpClient httpClient) => new(httpClient); +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveTestAttributes.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveTestAttributes.cs new file mode 100644 index 0000000..38df5dd --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveTestAttributes.cs @@ -0,0 +1,170 @@ +using System.Net.Sockets; + +namespace CareEvolution.Orchestrate.Tests.Helpers; + +internal static class LiveTestEnvironment +{ + private static readonly object SyncRoot = new(); + private static bool _loaded; + + public const string OrchestrateApiKey = "ORCHESTRATE_API_KEY"; + public const string IdentityApiKey = "ORCHESTRATE_IDENTITY_API_KEY"; + public const string IdentityMetricsKey = "ORCHESTRATE_IDENTITY_METRICS_KEY"; + public const string IdentityUrl = "ORCHESTRATE_IDENTITY_URL"; + public const string LocalHashingUrl = "ORCHESTRATE_IDENTITY_LOCAL_HASHING_URL"; + + public static string? GetSkipReason(params string[] requiredVariables) + { + EnsureEnvironmentLoaded(); + + var missingVariables = requiredVariables + .Where(variable => + string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(variable)) + ) + .ToArray(); + + if (missingVariables.Length == 0) + { + return GetUnavailableServiceReason(requiredVariables); + } + + return $"Live test requires environment variables: {string.Join(", ", missingVariables)}"; + } + + private static string? GetUnavailableServiceReason(IEnumerable requiredVariables) + { + foreach (var variable in requiredVariables) + { + if (variable is not (IdentityUrl or LocalHashingUrl)) + { + continue; + } + + var rawUrl = Environment.GetEnvironmentVariable(variable); + if ( + string.IsNullOrWhiteSpace(rawUrl) + || !Uri.TryCreate(rawUrl, UriKind.Absolute, out var uri) + ) + { + continue; + } + + if (!IsLocalHost(uri.Host)) + { + continue; + } + + var port = uri.IsDefaultPort + ? uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) + ? 443 + : 80 + : uri.Port; + + if (!CanConnect(uri.Host, port)) + { + return $"Live test requires {variable} service at {uri.Host}:{port} to be running."; + } + } + + return null; + } + + private static bool IsLocalHost(string host) => + host.Equals("localhost", StringComparison.OrdinalIgnoreCase) + || host.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase) + || host.Equals("::1", StringComparison.OrdinalIgnoreCase); + + private static bool CanConnect(string host, int port) + { + try + { + using var client = new TcpClient(); + var connectTask = client.ConnectAsync(host, port); + return connectTask.Wait(TimeSpan.FromMilliseconds(250)) && client.Connected; + } + catch + { + return false; + } + } + + private static void EnsureEnvironmentLoaded() + { + if (_loaded) + { + return; + } + + lock (SyncRoot) + { + if (_loaded) + { + return; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + var envPath = Path.Combine(current.FullName, ".env"); + if (File.Exists(envPath)) + { + LoadDotEnv(envPath); + break; + } + + current = current.Parent; + } + + _loaded = true; + } + } + + private static void LoadDotEnv(string path) + { + foreach (var rawLine in File.ReadAllLines(path)) + { + var line = rawLine.Trim(); + if (string.IsNullOrWhiteSpace(line) || line.StartsWith('#')) + { + continue; + } + + var separatorIndex = line.IndexOf('='); + if (separatorIndex <= 0) + { + continue; + } + + var key = line[..separatorIndex].Trim(); + var value = line[(separatorIndex + 1)..].Trim(); + if ( + value.Length >= 2 + && ( + (value.StartsWith('"') && value.EndsWith('"')) + || (value.StartsWith('\'') && value.EndsWith('\'')) + ) + ) + { + value = value[1..^1]; + } + + Environment.SetEnvironmentVariable(key, value); + } + } +} + +public sealed class LiveFactAttribute : FactAttribute +{ + public LiveFactAttribute(params string[] requiredVariables) + { + Skip = LiveTestEnvironment.GetSkipReason(requiredVariables); + } +} + +public sealed class LiveTheoryAttribute : TheoryAttribute +{ + public LiveTheoryAttribute(params string[] requiredVariables) + { + Skip = LiveTestEnvironment.GetSkipReason(requiredVariables); + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveTestData.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveTestData.cs new file mode 100644 index 0000000..b16415e --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/Helpers/LiveTestData.cs @@ -0,0 +1,84 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Hl7.Fhir.Serialization; + +namespace CareEvolution.Orchestrate.Tests.Helpers; + +internal static class LiveTestData +{ + private static readonly string BasePath = Path.Combine(AppContext.BaseDirectory, "LiveData"); + private static readonly FhirJsonFastParser FhirParser = new( + new ParserSettings(global::Hl7.Fhir.Model.Version.R4) + { + AllowUnrecognizedEnums = true, + AcceptUnknownMembers = true, + PermissiveParsing = true, + } + ); + + public static string Cda => ReadText("cda.xml"); + + public static string EncodingCda => ReadText("encoding_cda.xml"); + + public static string Hl7 => ReadText("hl7.txt"); + + public static Bundle R4Bundle => ReadBundle("r4_bundle.json"); + + public static Bundle RiskProfileBundle => ReadBundle("risk_profile_bundle.json"); + + public static string X12Document => ReadText("x12.txt"); + + public static JsonNode Dstu2Bundle => ReadJsonNode("dstu2_bundle.json"); + + public static JsonNode Stu3Bundle => ReadJsonNode("stu3_bundle.json"); + + public static Bundle NemsisBundle => ReadBundle("nemsis_bundle.json"); + + public static string BlindedDemographicData => + "H4sIAAAAAAAEA5yX2ZKj2BGG36WuO4Z9m4i5EItAbALEbs8FYkfsq6Cj392apiasmtK4" + + "bHODdCL4+PPkfzKT729z3A95U7/9Cn17S/K4jIa3X//x/W1c2/jt1zf2TL99e8uCIXv8oTZPz" + + "2FVw3H0gEU8aEbMNdFaLIRqQWYMiLn4bL9lt4Ajf3s81cZ9NY3B+KDvzHfMMt217WwgXfHPCQ" + + "Rh+jKzx3b/WQox5ETdglwmMNmX+v3Gcaifm2UeN3+gh7zKy6DPx/Wh+8e3738qVI+0T/t1kEU" + + "ASRKyP3mFgvG6Wjr98c5TZ6OnzmqlQZ54+isG/AXHnkhBrGAGLRWwlGq7Aj2NYHKuORA6n6ex" + + "5u18Ftm41HPhKjHhF7jtfHKq5XrhtS2+39cM2JnWZaLgZAvMWEZp2ypuXigdsTG7fYETZkkev" + + "btqEdZdOC+qqGrxcF7VAsAWYtQ4mvfwCgIrPdS/IN2xXcmBLEM4WCV2EaCUTgQ4mh0ZPCOiPH" + + "SghqulSXiqZP9M7PPu/2XbYNqswjqugSkEQ0RXBs0w9xcYzGI2XTzMYqqDZ6tADCy6gl+oC97" + + "FxWBVCpShKxMVQsG0Xq4hyfYsAhjjpazOHhsUZMr1X2XhrOMAxbg4PQF46B5aTSTjQ2Il6P4a" + + "qh2WVvN957IapuBSyhfqQvJQnG39UF2qJOpXAzO1Gt9RheooMAvOtCkAW38ISYNBvc84gnyyn" + + "Gw5Z0fcn1e55spn0HgAJ5Sf87xIMqWIOVeGBNCbYqRLuRce+YBznLrHV8UQFTd3vaoK2ku1w2" + + "ke3n+gHWiMNFAgfBkluXQfuC8kCuGxIjFQ0ANoJwTcflfKdoqN65RDfMDaSnvxbysHyPPdKl+" + + "ctA86YfUUEvhJInnGS6hZj+11C0f8fSPEPjNHqzkaNpzcVveQD19IHGvErI/1Avg8jyKpNcHX" + + "aTqtpaJ6+SE73vPrdDHcCaZ24YeXuN//SMt7CTzm/TCqQRX/uxA6ugpRQDkBCaY6knpd5WTTd" + + "17bGyclimWnNYhJqGBmXwbnn1b/u5I42tDmyCUMHW/WVseEdzu0OkQ1K7jhx9QuR/cEulKcAw" + + "D6Qu+Th+zBKoAOXd1HlZbVAs9jN99itbBA3YkgqBqbwOlhTd/EV/Z5As35LYAvs2wcvZ5nIrp" + + "as2DbcpKFvI4cDlgaUPgJv51sNv3PiphRsIwNcu4RJTTtrKVyiJySm2eVummYTi5xg47iFuoB" + + "L04v8aQI5jkbphI8ssb+Jqf7vjZu1SCQh9COtS9I59WEMwGyrc47QC+q3zMyFE+2oGGRjHoLa" + + "fmXAOzK2WtEDqH7MOuaijhhyVFcJuzVwXjS9n8b4rmUPjeyAGOHGjuBRMmlNk2pgghU7yFr4W" + + "naY81nOZl8p8ZmXMls84WVnztaPJgeuPjShTg4aaf1UXvtw3HQDytJS1pQsd1tCjBHlZlPLRb" + + "85Rnka+GWRCg2L4TY5ZELBlmFrOdcMI1pxA2VyWJoPHLuBHwB2qMA+E3lA5/01puixUvwCJ0o" + + "Mx3M/Mo93xOa7g1ggGqw/eSP5w1T+BDK71qVN5cI5y1jtoSlVeZGnUhaWKjOpFSj0REX0l6kE" + + "v95PTvDyNwalIy1M52IUeKI2qBEELWjfSqo5CotKVUoimn5afXC/p9xgY13JybRFpdgriJkoj" + + "4G+nv4dwTO+twouIghwUOWKPvyVXiRzs/glKUjRmulFTULLBJE3+Rrr70bxsW8Wpt0n9bV5bz" + + "bkVRfNDPiOasNbpHzEMG+HYmij7pM8d4kziZ0fq/2KL24knxg+PfJLSychXkxEnwAE4jLExxe" + + "Mo/xB6Ud0W3WWvNE656fbjdPrFGak4GjAs3SC7t8IC0kbOa45BwmxDof2w4tvMGuLySeTksZo" + + "5gEB2z1PjIsw/YiMx9wQUmEjLrBJamHoquuAiREV4rlp/dSotb2ORzT22PchS/W8Ml+j9HnqQ" + + "RUkdDHfQrgbFywTs3ZhZ91tFcrJ+ggbXLuDyt6OoMLfXqRCfyDMPc4eIqmeo9GS6UEoYCMMd6" + + "OQTYig0wpGQteJRLjYmCRXs1PH1A8EA1t0fotf09q4KYDQIGZxTGzhd1mJnEyGiLq4b6tt+5V" + + "c/1D2nM7lIO/dkNjJFkpSSGRmRx1MO784/hVFGBrjM6ALUnyeURbuFeYxs+j93c98H/E/F3lp" + + "EUTPSFpRJGk08SqzwRNz0Vu7fArCakWghmZyU2oV59ftFJkv9CnvALamlqKLzFlLm3WckXuc1" + + "EUjxzkZkAMaOMJ+TDzIIEAL5Lxgtc1smi148KRayjnUM6xWKVg1MS2U3qC03LPCzewt6W7wOy" + + "n6em56Jl1fWPrSdzQWIIiWTbU62QsXR6G1Hjo4MxyT5ozNUzI/1dF7za0t1gW/c3jVHVL4caB" + + "CuTGzvcVktr7lFIEuA4NV2X0i+Hhc22qjldJK3jVP7L0fQ+LdEq8DNt2MpWZhFiaNGCuUqA69" + + "K32U6SP8/Xzeq7L2NZrk0NcUFJieRCqICUmp2quu7u8bSWsOfYVYqN0wcNXuIeRf//2FkRzPj" + + "R9Hj++Sr+/5fUclHnExlWT9kGb5eHxz6/g33/8+BcAAAD//w=="; + + private static string ReadText(string fileName) => + File.ReadAllText(Path.Combine(BasePath, fileName)); + + private static Bundle ReadBundle(string fileName) => + (Bundle)FhirParser.Parse(ReadText(fileName), typeof(Bundle)); + + private static JsonNode ReadJsonNode(string fileName) => + JsonNode.Parse(ReadText(fileName)) + ?? throw new InvalidOperationException($"Unable to deserialize JSON fixture '{fileName}'."); +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/IdentityApiTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/IdentityApiTests.cs new file mode 100644 index 0000000..63f3fc6 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/IdentityApiTests.cs @@ -0,0 +1,206 @@ +using CareEvolution.Orchestrate.Tests.Helpers; + +namespace CareEvolution.Orchestrate.Tests; + +public sealed class IdentityApiTests +{ + [Fact] + public async Task AddOrUpdateRecordShouldEncodeRouteAndSendDemographicPayload() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_API_KEY"] = null, + ["ORCHESTRATE_IDENTITY_METRICS_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json( + """{"matchedPerson":{"id":"1","records":[],"version":1,"status":{"code":"Active","supersededBy":[]}},"changedPersons":[],"advisories":{"invalidDemographicFields":[]}}""" + ) + ) + ); + using var httpClient = new HttpClient(handler); + var api = new IdentityApi( + httpClient, + new IdentityApiOptions + { + Url = "https://identity.example.com", + ApiKey = "test-identity-api-key", + } + ); + + await api.AddOrUpdateRecordAsync( + new AddOrUpdateRecordRequest + { + Source = "source one", + Identifier = "id+/=", + Demographic = new Demographic + { + FirstName = "John", + LastName = "Doe", + MedicaidId = "12345", + }, + } + ); + + Assert.Equal( + "https://identity.example.com/mpi/v1/record/source%20one/id%2B%2F%3D", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + Assert.Contains("\"firstName\":\"John\"", handler.LastRequest.Body); + Assert.Contains("\"medicaidID\":\"12345\"", handler.LastRequest.Body); + } + + [Fact] + public async Task AddOrUpdateBlindedRecordShouldFlattenPayload() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_API_KEY"] = null, + ["ORCHESTRATE_IDENTITY_METRICS_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json( + """{"matchedPerson":{"id":"1","records":[],"version":1,"status":{"code":"Active","supersededBy":[]}},"changedPersons":[]}""" + ) + ) + ); + using var httpClient = new HttpClient(handler); + var api = new IdentityApi( + httpClient, + new IdentityApiOptions + { + Url = "https://identity.example.com", + ApiKey = "test-identity-api-key", + } + ); + + await api.AddOrUpdateBlindedRecordAsync( + new AddOrUpdateBlindedRecordRequest + { + Source = "source", + Identifier = "identifier", + BlindedDemographic = new BlindedDemographic { Data = "abc", Version = 1 }, + } + ); + + Assert.DoesNotContain( + "blindedDemographic", + handler.LastRequest!.Body, + StringComparison.OrdinalIgnoreCase + ); + Assert.Contains("\"data\":\"abc\"", handler.LastRequest.Body); + } + + [Fact] + public async Task DeleteRecordShouldSendEmptyObjectPayload() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_API_KEY"] = null, + ["ORCHESTRATE_IDENTITY_METRICS_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + var handler = new FakeHttpMessageHandler( + (_, _) => Task.FromResult(FakeResponses.Json("""{"changedPersons":[]}""")) + ); + using var httpClient = new HttpClient(handler); + var api = new IdentityApi( + httpClient, + new IdentityApiOptions + { + Url = "https://identity.example.com", + ApiKey = "test-identity-api-key", + } + ); + + await api.DeleteRecordAsync( + new CareEvolution.Orchestrate.Identity.Record + { + Source = "source", + Identifier = "identifier", + } + ); + + Assert.Equal("{}", handler.LastRequest!.Body); + } + + [Fact] + public async Task MonitoringShouldCallExpectedRoute() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_API_KEY"] = null, + ["ORCHESTRATE_IDENTITY_METRICS_KEY"] = null, + ["ORCHESTRATE_ADDITIONAL_HEADERS"] = null, + } + ); + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json( + """{"refreshed":"2026-01-01T00:00:00Z","totalRecordCount":1,"totalPersonCount":1,"globalMetricsRecords":[],"summaryMetricsRecords":[],"sourceTotals":[]}""" + ) + ) + ); + using var httpClient = new HttpClient(handler); + var api = new IdentityApi( + httpClient, + new IdentityApiOptions + { + Url = "https://identity.example.com", + ApiKey = "test-identity-api-key", + } + ); + + var response = await api.Monitoring.IdentifierMetricsAsync(); + + Assert.Equal(1, response.TotalRecordCount); + Assert.Equal( + "https://identity.example.com/monitoring/v1/identifierMetrics", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + } + + [Fact] + public async Task LocalHashingShouldUseConfiguredUrl() + { + using var environment = new EnvironmentVariableScope( + new Dictionary + { + ["ORCHESTRATE_IDENTITY_LOCAL_HASHING_URL"] = "https://hashing.example.com", + } + ); + + var handler = new FakeHttpMessageHandler( + (_, _) => + Task.FromResult( + FakeResponses.Json( + """{"data":"abc","version":1,"advisories":{"invalidDemographicFields":[]}}""" + ) + ) + ); + using var httpClient = new HttpClient(handler); + var api = new LocalHashingApi(httpClient); + + var response = await api.HashAsync(new Demographic { FirstName = "John" }); + + Assert.Equal("abc", response.Data); + Assert.Equal( + "https://hashing.example.com/hash", + handler.LastRequest!.RequestUri!.AbsoluteUri + ); + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveApiTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveApiTests.cs new file mode 100644 index 0000000..b8355af --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveApiTests.cs @@ -0,0 +1,1101 @@ +using System.IO.Compression; +using CareEvolution.Orchestrate.Exceptions; +using CareEvolution.Orchestrate.Tests.Helpers; + +namespace CareEvolution.Orchestrate.Tests; + +public sealed class LiveApiTests : IDisposable +{ + private static readonly byte[] PdfMagicNumber = [37, 80, 68, 70]; + private static readonly byte[] PkZipMagicNumber = [80, 75, 3, 4]; + private readonly HttpClient _httpClient = new(); + private readonly OrchestrateApi _api; + private static readonly ClassifyConditionRequest[] ClassifyConditionRequestItems = + [ + new ClassifyConditionRequest + { + Code = "119981000146107", + System = "http://snomed.info/sct", + }, + new ClassifyConditionRequest { Code = "119981000146107", System = "SNOMED" }, + ]; + private static readonly ClassifyMedicationRequest[] ClassifyMedicationRequestItems = + [ + new ClassifyMedicationRequest + { + Code = "2468231", + System = "http://www.nlm.nih.gov/research/umls/rxnorm", + }, + new ClassifyMedicationRequest { Code = "2468231", System = "RxNorm" }, + ]; + private static readonly ClassifyObservationRequest[] ClassifyObservationRequestItems = + [ + new ClassifyObservationRequest { Code = "94558-4", System = "http://loinc.org" }, + new ClassifyObservationRequest { Code = "94558-4", System = "LOINC" }, + ]; + private static readonly ( + StandardizeRequest Request, + string ExpectedCode + )[] StandardizeConditionCases = + [ + (new StandardizeRequest { Code = "370221004" }, "370221004"), + (new StandardizeRequest { Code = "J45.50" }, "J45.50"), + (new StandardizeRequest { Display = "dm2" }, "44054006"), + ]; + private static readonly ( + StandardizeRequest Request, + string ExpectedCode + )[] StandardizeLabCases = + [ + (new StandardizeRequest { Code = "4548-4" }, "4548-4"), + (new StandardizeRequest { Display = "hba1c 1/15/22 from outside lab" }, "43396009"), + ]; + private static readonly ( + StandardizeRequest Request, + string ExpectedCode + )[] StandardizeMedicationCases = + [ + (new StandardizeRequest { Code = "861004", System = "RxNorm" }, "861004"), + (new StandardizeRequest { Code = "59267-1000-02" }, "59267100002"), + ( + new StandardizeRequest + { + Display = "Jentadueto extended (linagliptin 2.5 / metFORMIN 1000mg)", + }, + "1796093" + ), + ]; + private static readonly ( + StandardizeRequest Request, + string ExpectedCode + )[] StandardizeObservationCases = + [ + (new StandardizeRequest { Code = "8480-6" }, "8480-6"), + (new StandardizeRequest { Display = "BMI" }, "39156-5"), + ]; + private static readonly ( + StandardizeRequest Request, + string ExpectedCode + )[] StandardizeProcedureCases = + [ + (new StandardizeRequest { Code = "80146002" }, "80146002"), + (new StandardizeRequest { Display = "ct head&neck" }, "429858000"), + ]; + private static readonly ( + StandardizeRequest Request, + string ExpectedCode + )[] StandardizeRadiologyCases = + [ + (new StandardizeRequest { Code = "711232001", System = "SNOMED" }, "711232001"), + ( + new StandardizeRequest { Display = "CT scan of head w/o iv contrast 3d ago@StJoes" }, + "30799-1" + ), + ]; + + public static TheoryData ClassifyConditionRequests => + new() { ClassifyConditionRequestItems[0], ClassifyConditionRequestItems[1] }; + + public static TheoryData ClassifyMedicationRequests => + new() { ClassifyMedicationRequestItems[0], ClassifyMedicationRequestItems[1] }; + + public static TheoryData ClassifyObservationRequests => + new() { ClassifyObservationRequestItems[0], ClassifyObservationRequestItems[1] }; + + public static TheoryData StandardizeConditionRequests => + new() + { + { StandardizeConditionCases[0].Request, StandardizeConditionCases[0].ExpectedCode }, + { StandardizeConditionCases[1].Request, StandardizeConditionCases[1].ExpectedCode }, + { StandardizeConditionCases[2].Request, StandardizeConditionCases[2].ExpectedCode }, + }; + + public static TheoryData StandardizeLabRequests => + new() + { + { StandardizeLabCases[0].Request, StandardizeLabCases[0].ExpectedCode }, + { StandardizeLabCases[1].Request, StandardizeLabCases[1].ExpectedCode }, + }; + + public static TheoryData StandardizeMedicationRequests => + new() + { + { StandardizeMedicationCases[0].Request, StandardizeMedicationCases[0].ExpectedCode }, + { StandardizeMedicationCases[1].Request, StandardizeMedicationCases[1].ExpectedCode }, + { StandardizeMedicationCases[2].Request, StandardizeMedicationCases[2].ExpectedCode }, + }; + + public static TheoryData StandardizeObservationRequests => + new() + { + { StandardizeObservationCases[0].Request, StandardizeObservationCases[0].ExpectedCode }, + { StandardizeObservationCases[1].Request, StandardizeObservationCases[1].ExpectedCode }, + }; + + public static TheoryData StandardizeProcedureRequests => + new() + { + { StandardizeProcedureCases[0].Request, StandardizeProcedureCases[0].ExpectedCode }, + { StandardizeProcedureCases[1].Request, StandardizeProcedureCases[1].ExpectedCode }, + }; + + public static TheoryData StandardizeRadiologyRequests => + new() + { + { StandardizeRadiologyCases[0].Request, StandardizeRadiologyCases[0].ExpectedCode }, + { StandardizeRadiologyCases[1].Request, StandardizeRadiologyCases[1].ExpectedCode }, + }; + + public LiveApiTests() + { + _api = LiveClients.CreateOrchestrateApi(_httpClient); + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(ClassifyConditionRequests))] + public async Task ClassifyConditionShouldClassifySingleRequest(ClassifyConditionRequest request) + { + var result = await _api.Terminology.ClassifyConditionAsync(request); + Assert.NotNull(result); + Assert.True(result.CciAcute); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ClassifyConditionShouldClassifyBatch() + { + var results = await _api.Terminology.ClassifyConditionAsync(ClassifyConditionRequestItems); + Assert.Equal(2, results.Count); + Assert.All(results, result => Assert.True(result.CciAcute)); + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(ClassifyMedicationRequests))] + public async Task ClassifyMedicationShouldClassifySingleRequest( + ClassifyMedicationRequest request + ) + { + var result = await _api.Terminology.ClassifyMedicationAsync(request); + Assert.True(result.RxNormGeneric); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ClassifyMedicationShouldClassifyBatch() + { + var results = await _api.Terminology.ClassifyMedicationAsync( + ClassifyMedicationRequestItems + ); + Assert.Equal(2, results.Count); + Assert.All(results, result => Assert.True(result.RxNormGeneric)); + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(ClassifyObservationRequests))] + public async Task ClassifyObservationShouldClassifySingleRequest( + ClassifyObservationRequest request + ) + { + var result = await _api.Terminology.ClassifyObservationAsync(request); + Assert.Equal("MICRO", result.LoincClass); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ClassifyObservationShouldClassifyBatch() + { + var results = await _api.Terminology.ClassifyObservationAsync( + ClassifyObservationRequestItems + ); + Assert.Equal(2, results.Count); + Assert.All(results, result => Assert.Equal("MICRO", result.LoincClass)); + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(StandardizeConditionRequests))] + public async Task StandardizeConditionShouldStandardizeSingleRequest( + StandardizeRequest request, + string expectedCode + ) + { + var result = await _api.Terminology.StandardizeConditionAsync(request); + Assert.Contains(result.Coding, coding => coding.Code == expectedCode); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeConditionShouldStandardizeBatch() + { + var requests = StandardizeConditionCases.Select(row => row.Request).ToList(); + var expected = StandardizeConditionCases.Select(row => row.ExpectedCode).ToList(); + var results = await _api.Terminology.StandardizeConditionAsync(requests); + Assert.Equal(3, results.Count); + for (var index = 0; index < results.Count; index++) + { + Assert.Contains(results[index].Coding, coding => coding.Code == expected[index]); + } + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(StandardizeLabRequests))] + public async Task StandardizeLabShouldStandardizeSingleRequest( + StandardizeRequest request, + string expectedCode + ) + { + var result = await _api.Terminology.StandardizeLabAsync(request); + Assert.Contains(result.Coding, coding => coding.Code == expectedCode); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeLabShouldStandardizeBatch() + { + var requests = StandardizeLabCases.Select(row => row.Request).ToList(); + var expected = StandardizeLabCases.Select(row => row.ExpectedCode).ToList(); + var results = await _api.Terminology.StandardizeLabAsync(requests); + Assert.Equal(2, results.Count); + for (var index = 0; index < results.Count; index++) + { + Assert.Contains(results[index].Coding, coding => coding.Code == expected[index]); + } + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(StandardizeMedicationRequests))] + public async Task StandardizeMedicationShouldStandardizeSingleRequest( + StandardizeRequest request, + string expectedCode + ) + { + var result = await _api.Terminology.StandardizeMedicationAsync(request); + Assert.Contains(result.Coding, coding => coding.Code == expectedCode); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeMedicationShouldStandardizeBatch() + { + var requests = StandardizeMedicationCases.Select(row => row.Request).ToList(); + var expected = StandardizeMedicationCases.Select(row => row.ExpectedCode).ToList(); + var results = await _api.Terminology.StandardizeMedicationAsync(requests); + Assert.Equal(3, results.Count); + for (var index = 0; index < results.Count; index++) + { + Assert.Contains(results[index].Coding, coding => coding.Code == expected[index]); + } + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(StandardizeObservationRequests))] + public async Task StandardizeObservationShouldStandardizeSingleRequest( + StandardizeRequest request, + string expectedCode + ) + { + var result = await _api.Terminology.StandardizeObservationAsync(request); + Assert.Contains(result.Coding, coding => coding.Code == expectedCode); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeObservationShouldStandardizeBatch() + { + var requests = StandardizeObservationCases.Select(row => row.Request).ToList(); + var expected = StandardizeObservationCases.Select(row => row.ExpectedCode).ToList(); + var results = await _api.Terminology.StandardizeObservationAsync(requests); + Assert.Equal(2, results.Count); + for (var index = 0; index < results.Count; index++) + { + Assert.Contains(results[index].Coding, coding => coding.Code == expected[index]); + } + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(StandardizeProcedureRequests))] + public async Task StandardizeProcedureShouldStandardizeSingleRequest( + StandardizeRequest request, + string expectedCode + ) + { + var result = await _api.Terminology.StandardizeProcedureAsync(request); + Assert.Contains(result.Coding, coding => coding.Code == expectedCode); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeProcedureShouldStandardizeBatch() + { + var requests = StandardizeProcedureCases.Select(row => row.Request).ToList(); + var expected = StandardizeProcedureCases.Select(row => row.ExpectedCode).ToList(); + var results = await _api.Terminology.StandardizeProcedureAsync(requests); + Assert.Equal(2, results.Count); + for (var index = 0; index < results.Count; index++) + { + Assert.Contains(results[index].Coding, coding => coding.Code == expected[index]); + } + } + + [LiveTheory(LiveTestEnvironment.OrchestrateApiKey)] + [MemberData(nameof(StandardizeRadiologyRequests))] + public async Task StandardizeRadiologyShouldStandardizeSingleRequest( + StandardizeRequest request, + string expectedCode + ) + { + var result = await _api.Terminology.StandardizeRadiologyAsync(request); + Assert.Contains(result.Coding, coding => coding.Code == expectedCode); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeRadiologyShouldStandardizeBatch() + { + var requests = StandardizeRadiologyCases.Select(row => row.Request).ToList(); + var expected = StandardizeRadiologyCases.Select(row => row.ExpectedCode).ToList(); + var results = await _api.Terminology.StandardizeRadiologyAsync(requests); + Assert.Equal(2, results.Count); + for (var index = 0; index < results.Count; index++) + { + Assert.Contains(results[index].Coding, coding => coding.Code == expected[index]); + } + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task StandardizeBundleShouldStandardize() + { + var result = await _api.Terminology.StandardizeBundleAsync(LiveTestData.R4Bundle); + Assert.NotNull(result.Entry); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithoutPatientShouldConvert() + { + var result = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = LiveTestData.Hl7 } + ); + Assert.NotNull(result.Entry); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithPatientShouldConvert() + { + var result = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = LiveTestData.Hl7, PatientId = "12/34" } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.Equal("12/34", patient.Id); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithPatientIdentifierAndSystemShouldConvert() + { + var result = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request + { + Content = LiveTestData.Hl7, + PatientIdentifier = "1234", + PatientIdentifierSystem = "GoodHealthClinic", + } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.True( + patient.Identifier?.Count > 0 + && patient.Identifier[0].Use == "usual" + && patient + .Identifier[0] + .System?.Contains("GoodHealthClinic", StringComparison.Ordinal) == true + && patient.Identifier[0].Value == "1234" + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithTimezoneShouldConvert() + { + var result = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = LiveTestData.Hl7, Tz = "America/New_York" } + ); + var encounter = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Encounter + ); + Assert.Equal("2014-11-07T14:40:00-05:00", encounter.Period?.Start); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithoutTimezoneShouldPresumeUtc() + { + var result = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = LiveTestData.Hl7 } + ); + var encounter = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Encounter + ); + Assert.Equal("2014-11-07T14:40:00+00:00", encounter.Period?.Start); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithLabProcessingHintShouldConvert() + { + const string content = """ + MSH|^~\&|||||20220309050000||ORU^R01|||2.7 + PID|1||123456||LastName^FirstName|||||||||||||5678 + PV1|1|I||||||||||||||||||||||||||||||||||||||||||20220309050000 + OBR||001CCK612||ABC^AUTOMATED BLOOD COUNT^LAB|||20220309134200|||2222^ORDERED,BY||||20220309134400|Specimen^|10341^Doctor MD^First^F||||W2622||||HE|F|CBC^ABC|^^^^^R|^~^~^||||||| + NTE|||Lab Report Comment + OBX|1|ST|^WBC^LAB|1|1.4|K/UL|4.5-11.5|L^LL|||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|2|ST|^RBC^LAB|1|3.50|M/UL|4.3-5.9|L|||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|3|ST|^HGB^LAB|1|11.6|GM/DL|13.9-16.3|L|||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|4|ST|^HCT^LAB|1|33.7|%|39-55|L|||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|5|ST|^MCV^LAB|1|96.4|FL|80-100||||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|6|ST|^MCH^LAB|1|33.1|PG|25.4-34.6||||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|7|ST|^MCHC^LAB|1|34.3|GM/DL|30-37||||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|8|ST|^RDW^LAB|1|17.9|%|11.5-14.5|H|||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + OBX|9|ST|^PLATELETS^LAB|1|125|K/UL|130-400|L|||F|||202203091347|R^ROUTINE LAB|2222^ORDERED,BY| + """; + + var hintedResult = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = content, ProcessingHint = "lab" } + ); + var unhintedResult = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = content } + ); + var defaultResult = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = content, ProcessingHint = "default" } + ); + + Assert.Equal(9, CountResources(hintedResult, Hl7.Fhir.Model.ResourceType.Observation)); + Assert.Equal(0, CountResources(unhintedResult, Hl7.Fhir.Model.ResourceType.Observation)); + Assert.Equal(0, CountResources(defaultResult, Hl7.Fhir.Model.ResourceType.Observation)); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertHl7ToFhirR4WithTranscriptionProcessingHintShouldConvert() + { + const string content = """ + MSH|^~\&||TX|||20110706100000||ORU^R01|||2.3 + PID|1||123456||LastName^FirstName||20000101|M||||||||||7890 + PV1|1|I||||||||||||||||||||||||||||||||||||||||||20110706100000 + ORC|RE|^SCM|||||||20110706100000|||010400^DOE MD^JOHN^^^^ + OBR|1|^SCM|001XYZ555^SCM|CH9^CHEST SPECIAL VIEWS|||20110706100000|||||||20110706100000||010400^DOE MD^JOHN^^^^|||||||||P||^^^20110706100000^^R|~~~~||||010400^DOE MD^JOHN|~|^UNKNOWN^TECHNOLOGIST^^^^|010400^DOE MD^JOHN^^^^ + OBX|1|ST|&GDT^^GDT||Line 1||||||F + OBX|2|ST|&GDT^Label^GDT||Line 2||||||F + OBX|3|ST|&GDT^&Not a Label^GDT||Line 3||||||F + OBX|4|ST|Dictation TS|2|Dictated by: Tue Mar 18, 2025 1:06:45 PM EDT [INTERFACE, INCOMING RADIANT IMAGE AVAILABILITY]||||||Final|||||E175762^MILLER^AMANDA^^^^^^PROVID^^^^PROVID^^^^^^^^RT||||||||| + """; + + var hintedResult = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = content, ProcessingHint = "transcription" } + ); + var unhintedResult = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = content } + ); + var defaultResult = await _api.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = content, ProcessingHint = "default" } + ); + + Assert.Equal(1, CountResources(hintedResult, Hl7.Fhir.Model.ResourceType.Binary)); + Assert.Equal(0, CountResources(unhintedResult, Hl7.Fhir.Model.ResourceType.Binary)); + Assert.Equal(0, CountResources(defaultResult, Hl7.Fhir.Model.ResourceType.Binary)); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToFhirR4WithoutPatientShouldConvert() + { + var result = await _api.Convert.CdaToFhirR4Async( + new ConvertCdaToFhirR4Request { Content = LiveTestData.Cda } + ); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToFhirR4WithIncludeOriginalCdaShouldConvert() + { + var result = await _api.Convert.CdaToFhirR4Async( + new ConvertCdaToFhirR4Request { Content = LiveTestData.Cda, IncludeOriginalCda = true } + ); + Assert.Contains( + result.Entry, + entry => + entry.Resource?.ResourceType == Hl7.Fhir.Model.ResourceType.DocumentReference + && entry.Resource is Hl7.Fhir.Model.R4.DocumentReference doc + && doc.Type?.Coding?.Any(coding => coding.Code == "Cda") == true + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToFhirR4WithIncludeStandardizedCdaShouldConvert() + { + var result = await _api.Convert.CdaToFhirR4Async( + new ConvertCdaToFhirR4Request + { + Content = LiveTestData.Cda, + IncludeStandardizedCda = true, + } + ); + Assert.Contains( + result.Entry, + entry => + entry.Resource?.ResourceType == Hl7.Fhir.Model.ResourceType.DocumentReference + && entry.Resource is Hl7.Fhir.Model.R4.DocumentReference doc + && doc.Type?.Coding?.Any(coding => coding.Code == "StandardizedCda") == true + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToFhirR4WithPatientShouldConvert() + { + var result = await _api.Convert.CdaToFhirR4Async( + new ConvertCdaToFhirR4Request { Content = LiveTestData.Cda, PatientId = "1234" } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.Equal("1234", patient.Id); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToFhirR4WithPatientIdentifierAndSystemShouldConvert() + { + var result = await _api.Convert.CdaToFhirR4Async( + new ConvertCdaToFhirR4Request + { + Content = LiveTestData.Cda, + PatientIdentifier = "1234", + PatientIdentifierSystem = "GoodHealthClinic", + } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.True( + patient.Identifier?.Count > 0 + && patient.Identifier[0].Use == "usual" + && patient + .Identifier[0] + .System?.Contains("GoodHealthClinic", StringComparison.Ordinal) == true + && patient.Identifier[0].Value == "1234" + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToPdfShouldConvert() + { + var result = await _api.Convert.CdaToPdfAsync( + new ConvertCdaToPdfRequest { Content = LiveTestData.Cda } + ); + Assert.Equal(PdfMagicNumber, result.Take(4).ToArray()); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertFhirR4ToCdaShouldConvert() + { + var result = await _api.Convert.FhirR4ToCdaAsync( + new ConvertFhirR4ToCdaRequest { Content = LiveTestData.R4Bundle } + ); + Assert.StartsWith("( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.Equal("1234", patient.Id); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCombinedFhirR4BundlesWithPatientIdentifierAndSystemShouldConvert() + { + var request = ConvertRequestFactory.GenerateConvertCombinedFhirBundlesRequestFromBundles([ + LiveTestData.R4Bundle, + LiveTestData.R4Bundle, + ]); + request.PatientIdentifier = "1234"; + request.PatientIdentifierSystem = "GoodHealthClinic"; + + var result = await _api.Convert.CombineFhirR4BundlesAsync(request); + Assert.Equal(2, result.Entry.Count); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.True( + patient.Identifier?.Count > 0 + && patient.Identifier[0].Use == "usual" + && patient + .Identifier[0] + .System?.Contains("GoodHealthClinic", StringComparison.Ordinal) == true + && patient.Identifier[0].Value == "1234" + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertX12ToFhirR4ShouldReturnBundle() + { + var result = await _api.Convert.X12ToFhirR4Async( + new ConvertX12ToFhirR4Request { Content = LiveTestData.X12Document } + ); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertX12ToFhirR4WithPatientShouldReturnBundle() + { + var result = await _api.Convert.X12ToFhirR4Async( + new ConvertX12ToFhirR4Request + { + Content = LiveTestData.X12Document, + PatientId = "12/34", + } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.Equal("12/34", patient.Id); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertX12ToFhirR4WithPatientIdentifierAndSystemShouldConvert() + { + var result = await _api.Convert.X12ToFhirR4Async( + new ConvertX12ToFhirR4Request + { + Content = LiveTestData.X12Document, + PatientIdentifier = "1234", + PatientIdentifierSystem = "GoodHealthClinic", + } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.True( + patient.Identifier?.Count > 0 + && patient.Identifier[0].Use == "usual" + && patient + .Identifier[0] + .System?.Contains("GoodHealthClinic", StringComparison.Ordinal) == true + && patient.Identifier[0].Value == "1234" + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4CodeSystemShouldReturnCodeSystem() + { + var result = await _api.Terminology.GetFhirR4CodeSystemAsync( + new GetFhirR4CodeSystemRequest { CodeSystem = "SNOMED" } + ); + Assert.NotEmpty(result.Concept); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4CodeSystemWithPageShouldReturnCodeSystem() + { + var result = await _api.Terminology.GetFhirR4CodeSystemAsync( + new GetFhirR4CodeSystemRequest + { + CodeSystem = "SNOMED", + PageNumber = 0, + PageSize = 2, + } + ); + Assert.NotEmpty(result.Concept); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4CodeSystemWithSearchShouldReturnCodeSystem() + { + var result = await _api.Terminology.GetFhirR4CodeSystemAsync( + new GetFhirR4CodeSystemRequest + { + CodeSystem = "ICD-10-CM", + ConceptContains = "myocardial infarction", + PageNumber = 0, + PageSize = 2, + } + ); + Assert.NotEmpty(result.Concept); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task SummarizeFhirR4CodeSystemsShouldReturnBundle() + { + var result = await _api.Terminology.SummarizeFhirR4CodeSystemsAsync(); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ConceptMapsShouldReturnBundle() + { + var result = await _api.Terminology.GetFhirR4ConceptMapsAsync(); + Assert.NotEmpty(result.Entry); + Assert.All( + result.Entry, + entry => + Assert.Equal(Hl7.Fhir.Model.ResourceType.ConceptMap, entry.Resource?.ResourceType) + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task TranslateFhirR4ConceptMapWithCodeShouldTranslate() + { + var result = await _api.Terminology.TranslateFhirR4ConceptMapAsync( + new TranslateFhirR4ConceptMapRequest { Code = "119981000146107" } + ); + Assert.NotEmpty(result.Parameter); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task TranslateFhirR4ConceptMapWithCodeAndDomainShouldTranslate() + { + var result = await _api.Terminology.TranslateFhirR4ConceptMapAsync( + new TranslateFhirR4ConceptMapRequest { Code = "119981000146107", Domain = "Condition" } + ); + Assert.NotEmpty(result.Parameter); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task SummarizeFhirR4ValueSetScopeShouldReturnBundle() + { + var result = await _api.Terminology.SummarizeFhirR4ValueSetScopeAsync( + new SummarizeFhirR4ValueSetScopeRequest { Scope = "http://loinc.org" } + ); + Assert.NotEmpty(result.Entry); + Assert.True(result.Entry.Count <= 10000); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetShouldReturnValueSet() + { + var result = await _api.Terminology.GetFhirR4ValueSetAsync( + new GetFhirR4ValueSetRequest + { + Id = "00987FA2EDADBD0E43DA59E171B80F99DBF832C69904489EE6F9E6450925E5A2", + } + ); + Assert.NotNull(result.Compose?.Include); + Assert.NotEmpty(result.Compose.Include); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task SummarizeFhirR4ValueSetShouldReturnValueSet() + { + var result = await _api.Terminology.SummarizeFhirR4ValueSetAsync( + new SummarizeFhirR4ValueSetRequest + { + Id = "00987FA2EDADBD0E43DA59E171B80F99DBF832C69904489EE6F9E6450925E5A2", + } + ); + Assert.Equal(Hl7.Fhir.Model.ResourceType.ValueSet, result.ResourceType); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetScopesShouldReturnValueSet() + { + var result = await _api.Terminology.GetFhirR4ValueSetScopesAsync(); + Assert.NotNull(result.Compose?.Include); + Assert.NotEmpty(result.Compose.Include); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetsByScopeWithoutPaginationShouldRaise() + { + await Assert.ThrowsAsync(() => + _api.Terminology.GetFhirR4ValueSetsByScopeAsync( + new GetFhirR4ValueSetsByScopeRequest { Scope = "http://loinc.org" } + ) + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetsByScopeWithPageAndScopeShouldReturnBundle() + { + var result = await _api.Terminology.GetFhirR4ValueSetsByScopeAsync( + new GetFhirR4ValueSetsByScopeRequest + { + Scope = "http://loinc.org", + PageNumber = 0, + PageSize = 2, + } + ); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetsByScopeWithPageAndNameShouldReturnBundle() + { + var result = await _api.Terminology.GetFhirR4ValueSetsByScopeAsync( + new GetFhirR4ValueSetsByScopeRequest + { + Name = "LP7839-6", + PageNumber = 0, + PageSize = 2, + } + ); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetsByScopeWithPageNameAndScopeShouldReturnBundle() + { + var result = await _api.Terminology.GetFhirR4ValueSetsByScopeAsync( + new GetFhirR4ValueSetsByScopeRequest + { + Name = "LP7839-6", + Scope = "http://loinc.org", + PageNumber = 0, + PageSize = 2, + } + ); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetFhirR4ValueSetsByScopeWithJustPageShouldReturnBundle() + { + var result = await _api.Terminology.GetFhirR4ValueSetsByScopeAsync( + new GetFhirR4ValueSetsByScopeRequest { PageNumber = 0, PageSize = 2 } + ); + Assert.NotEmpty(result.Entry); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task SummarizeFhirR4CodeSystemShouldReturnCodeSystem() + { + var result = await _api.Terminology.SummarizeFhirR4CodeSystemAsync( + new SummarizeFhirR4CodeSystemRequest { CodeSystem = "SNOMED" } + ); + Assert.True(result.Count > 0); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task GetAllFhirR4ValueSetsForCodesShouldReturnParameters() + { + var parameters = new Parameters + { + Parameter = + [ + new Parameters.ParameterComponent + { + Name = "code", + Value = new Hl7.Fhir.Model.FhirString("119981000146107"), + }, + new Parameters.ParameterComponent + { + Name = "system", + Value = new Hl7.Fhir.Model.FhirString("http://snomed.info/sct"), + }, + ], + }; + + var result = await _api.Terminology.GetAllFhirR4ValueSetsForCodesAsync(parameters); + Assert.NotEmpty(result.Parameter); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertFhirDstu2ToFhirR4ShouldConvert() + { + var result = await _api.Convert.FhirDstu2ToFhirR4Async( + new ConvertFhirDstu2ToFhirR4Request { Content = LiveTestData.Dstu2Bundle } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.Contains( + patient.Identifier, + identifier => identifier.ElementId == "id3" && identifier.Value == "12345A" + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertFhirStu3ToFhirR4ShouldConvert() + { + var result = await _api.Convert.FhirStu3ToFhirR4Async( + new ConvertFhirStu3ToFhirR4Request { Content = LiveTestData.Stu3Bundle } + ); + var patient = GetEntryResourceByType( + result, + Hl7.Fhir.Model.ResourceType.Patient + ); + Assert.Contains( + patient.Identifier, + identifier => identifier.ElementId == "id3" && identifier.Value == "1234A" + ); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertFhirR4ToHealthLakeShouldConvert() + { + var result = await _api.Convert.FhirR4ToHealthLakeAsync( + new ConvertFhirR4ToHealthLakeRequest { Content = LiveTestData.R4Bundle } + ); + Assert.Equal(Hl7.Fhir.Model.BundleType.Collection, result.Type); + Assert.Equal(Hl7.Fhir.Model.ResourceType.Bundle, result.Entry[0].Resource?.ResourceType); + Assert.Equal(Hl7.Fhir.Model.BundleType.Batch, ((Bundle)result.Entry[0].Resource!).Type); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertCdaToHtmlShouldConvert() + { + var result = await _api.Convert.CdaToHtmlAsync( + new ConvertCdaToHtmlRequest { Content = LiveTestData.Cda } + ); + Assert.StartsWith(" entry.FullName).ToList(); + Assert.Contains("patients.csv", names); + Assert.Contains("encounters.csv", names); + Assert.Contains("procedures.csv", names); + Assert.Contains("conditions.csv", names); + Assert.All( + archive.Entries, + entry => + { + Assert.EndsWith(".csv", entry.FullName, StringComparison.Ordinal); + Assert.True(entry.Length > 0); + } + ); + + using var reader = new StreamReader(archive.GetEntry("patients.csv")!.Open()); + var content = await reader.ReadToEndAsync(); + Assert.Contains(",", content, StringComparison.Ordinal); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task ConvertFhirR4ToManifestWithDelimiterShouldHaveCsvsAndExpectedDelimiter() + { + var result = await _api.Convert.FhirR4ToManifestAsync( + new ConvertFhirR4ToManifestRequest { Content = LiveTestData.R4Bundle, Delimiter = "|" } + ); + + using var archive = new ZipArchive(new MemoryStream(result), ZipArchiveMode.Read); + using var reader = new StreamReader(archive.GetEntry("patients.csv")!.Open()); + var content = await reader.ReadToEndAsync(); + Assert.Contains("|", content, StringComparison.Ordinal); + } + + [LiveFact(LiveTestEnvironment.OrchestrateApiKey)] + public async Task WithTimeoutShouldTimeout() + { + using var httpClient = new HttpClient(); + var timeoutApi = new OrchestrateApi( + httpClient, + new OrchestrateClientOptions { TimeoutMs = 1 } + ); + await Assert.ThrowsAnyAsync(() => + timeoutApi.Convert.Hl7ToFhirR4Async( + new ConvertHl7ToFhirR4Request { Content = LiveTestData.Hl7 } + ) + ); + } + + private static int CountResources(Bundle bundle, Hl7.Fhir.Model.ResourceType resourceType) => + bundle.Entry.Count(entry => entry.Resource?.ResourceType == resourceType); + + private static TResource GetEntryResourceByType( + Bundle bundle, + Hl7.Fhir.Model.ResourceType resourceType + ) + where TResource : Hl7.Fhir.Model.Resource + { + return bundle.Entry.First(entry => entry.Resource?.ResourceType == resourceType).Resource + as TResource + ?? throw new InvalidOperationException($"Expected resource type '{resourceType}'."); + } + + public void Dispose() + { + _httpClient.Dispose(); + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/cda.xml b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/cda.xml new file mode 100644 index 0000000..027565b --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/cda.xml @@ -0,0 +1,70 @@ + + + + + + + + + + Medical Summary Document + + + + + + + + 34 Drury Lane + Disney Land + CA + 90210 + + + + + Patient + Smith + + + + + + + + + + + + +
+ + + + + Vital Signs + No Vital Signs Available + + + + + + + + + + + + + + + + + + + +
+
+
+
+
diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/dstu2_bundle.json b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/dstu2_bundle.json new file mode 100644 index 0000000..9203f39 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/dstu2_bundle.json @@ -0,0 +1,2070 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "entry": [ + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "resource": { + "resourceType": "Patient", + "id": "3ead5e15-4da5-480b-8a94-ffea1e936809", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T15:08:05.723+00:00", + "profile": [ + "http://fhir.org/guides/argonaut/StructureDefinition/argo-patient" + ], + "security": [ + { + "system": "http://careevolution.com/accesspolicyname", + "code": "Standard Record Policy" + } + ] + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#patientInstitutions", + "valueCoding": { + "id": "patient-institution1", + "code": "BeaconInstitution", + "display": "BeaconInstitution" + } + } + ], + "identifier": [ + { + "id": "id1", + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest", + "value": "12345A" + }, + { + "id": "id2", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN", + "value": "12345A" + }, + { + "id": "id3", + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "name": [ + { + "id": "name1", + "use": "official", + "family": [ + "Smith" + ], + "given": [ + "John" + ] + } + ], + "gender": "male", + "_gender": { + "id": "gender1", + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "id": "gender2", + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/Gender", + "code": "M", + "display": "Male", + "userSelected": true + } + ] + } + } + ] + }, + "birthDate": "1961-01-14", + "_birthDate": { + "id": "birthdate1" + }, + "deceasedBoolean": false, + "_deceasedBoolean": { + "id": "deceased1" + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/0-3a4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "0-3a4295efae7fed11b9cc0e32e07a5c1b", + "contained": [ + { + "resourceType": "Patient", + "id": "patient", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "profile": [ + "http://fhir.org/guides/argonaut/StructureDefinition/argo-patient" + ], + "security": [ + { + "system": "http://careevolution.com/accesspolicyname", + "code": "Standard Record Policy" + } + ] + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#patientInstitutions", + "valueCoding": { + "id": "patient-institution1", + "code": "BeaconInstitution", + "display": "BeaconInstitution" + } + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest", + "value": "12345A" + }, + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN", + "value": "12345A" + }, + { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "name": [ + { + "use": "official", + "_use": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/NameType", + "code": "LegalName", + "display": "Legal Name", + "userSelected": true + } + ] + } + } + ] + }, + "family": [ + "Smith" + ], + "given": [ + "John" + ] + } + ], + "gender": "male", + "_gender": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/Gender", + "code": "M", + "display": "Male", + "userSelected": true + } + ] + } + } + ] + }, + "birthDate": "1961-01-14", + "deceasedBoolean": false + } + ], + "target": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#id1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#id2" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#id3" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#name1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#birthdate1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#deceased1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#gender1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#gender2" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#patient-institution1" + } + ], + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + } + ], + "period": { + "start": "2022-12-19T15:08:05.723+00:00", + "end": "2022-12-19T15:08:05.717+00:00" + }, + "recorded": "2022-12-19T15:08:05.717+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "UPDATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ], + "entity": [ + { + "role": "source", + "type": { + "system": "http://hl7.org/fhir/resource-types", + "code": "Patient" + }, + "reference": "#patient" + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Condition/5.d30a4c15f97fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Condition", + "id": "5.d30a4c15f97fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T23:58:55.387+00:00" + }, + "patient": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisCode", + "code": "flu", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "6142004", + "display": "Influenza (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "J11.1", + "display": "Influenza due to unidentified influenza virus with other respiratory manifestations", + "userSelected": false + } + ] + }, + "category": { + "coding": [ + { + "system": "http://argonaut.hl7.org", + "code": "health-concern" + }, + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisType", + "code": "EncounterDiagnosis", + "userSelected": true + }, + { + "system": "http://fhir.carevolution.com/codes/fhir-diagnosis-role/Reference", + "code": "CC", + "display": "Chief complaint", + "userSelected": false + } + ] + }, + "clinicalStatus": "resolved", + "_clinicalStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisStatus", + "code": "resolved", + "userSelected": true + } + ] + } + } + ] + }, + "_verificationStatus": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", + "valueCode": "unsupported" + }, + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisVerificationStatus", + "code": "unconfirmed", + "userSelected": true + } + ] + } + } + ] + }, + "onsetDateTime": "2022-12-19T02:31:55.382-05:00" + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Condition/5.3d4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Condition", + "id": "5.3d4295efae7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T15:08:05.75+00:00", + "profile": [ + "http://fhir.org/guides/argonaut/StructureDefinition/argo-condition" + ] + }, + "patient": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisCode", + "code": "multiple sclerosis", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "24700007", + "display": "Multiple sclerosis (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "G35", + "display": "Multiple sclerosis", + "userSelected": false + } + ] + }, + "category": { + "coding": [ + { + "system": "http://argonaut.hl7.org", + "code": "health-concern" + }, + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisType", + "code": "EncounterDiagnosis", + "userSelected": true + }, + { + "system": "http://fhir.carevolution.com/codes/fhir-diagnosis-role/Reference", + "code": "CC", + "display": "Chief complaint", + "userSelected": false + } + ] + }, + "clinicalStatus": "resolved", + "_clinicalStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisStatus", + "code": "inactive", + "userSelected": true + } + ] + } + } + ] + }, + "verificationStatus": "confirmed", + "_verificationStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisVerificationStatus", + "code": "confirmed", + "userSelected": true + } + ] + } + } + ] + }, + "onsetDateTime": "2022-12-18T18:55:05.675-05:00" + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Condition/5.3c4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Condition", + "id": "5.3c4295efae7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T15:08:05.74+00:00" + }, + "patient": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisCode", + "code": "crohns disease", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "34000006", + "display": "Crohn's disease (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "K50.90", + "display": "Crohn's disease, unspecified, without complications", + "userSelected": false + } + ] + }, + "category": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", + "valueCode": "unsupported" + } + ], + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisType", + "code": "SECONDARY", + "display": "SECONDARY", + "userSelected": true + }, + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/DiagnosisType", + "code": "Secondary", + "display": "Secondary", + "userSelected": false + } + ] + }, + "clinicalStatus": "active", + "_clinicalStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisStatus", + "code": "Active", + "userSelected": true + } + ] + } + } + ] + }, + "verificationStatus": "confirmed", + "_verificationStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisVerificationStatus", + "code": "confirmed", + "userSelected": true + } + ] + } + } + ] + }, + "onsetDateTime": "2022-12-18T17:20:05.675-05:00" + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/4-d30a4c15f97fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "4-d30a4c15f97fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Condition/5.d30a4c15f97fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T23:58:55.387+00:00", + "end": "2022-12-19T23:58:55.387+00:00" + }, + "recorded": "2022-12-19T23:58:55.387+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/4-3d4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "4-3d4295efae7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Condition/5.3d4295efae7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T15:08:05.75+00:00", + "end": "2022-12-19T15:08:05.75+00:00" + }, + "recorded": "2022-12-19T15:08:05.75+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/4-3c4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "4-3c4295efae7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Condition/5.3c4295efae7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T15:08:05.74+00:00", + "end": "2022-12-19T15:08:05.74+00:00" + }, + "recorded": "2022-12-19T15:08:05.74+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.682d4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.682d4f56bc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:44:05.61+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-18T13:45:05.605-05:00", + "issued": "2022-12-18T13:45:05.605-05:00", + "valueQuantity": { + "value": 1690728474 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.d12b4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.d12b4f56bc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:44:03.04+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-18T14:56:03.025-05:00", + "issued": "2022-12-18T14:56:03.025-05:00", + "valueQuantity": { + "value": 1271126762 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.028d6f3ebc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.028d6f3ebc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:43:26.953+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-18T17:09:26.943-05:00", + "issued": "2022-12-18T17:09:26.943-05:00", + "valueQuantity": { + "value": 295302631 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.54e37a32bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.54e37a32bc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:43:10.48+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-19T10:31:10.464-05:00", + "issued": "2022-12-19T10:31:10.464-05:00", + "valueQuantity": { + "value": 652309659 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.d24b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.d24b67f6bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:41:23.93+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-18T11:44:23.919-05:00", + "issued": "2022-12-18T11:44:23.919-05:00", + "valueQuantity": { + "value": 789662810 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.a94b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.a94b67f6bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:41:22.403+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-18T11:56:22.403-05:00", + "issued": "2022-12-18T11:56:22.403-05:00", + "valueQuantity": { + "value": 1582118206 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.f1b562d2bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.f1b562d2bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:40:27.56+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-18T22:38:27.543-05:00", + "issued": "2022-12-18T22:38:27.543-05:00", + "valueQuantity": { + "value": 252600252 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.2d6720babb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.2d6720babb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-20T03:22:56.517+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-19T11:21:45.582-05:00", + "issued": "2022-12-19T11:21:45.582-05:00", + "valueQuantity": { + "value": 1724700070 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.b185f6a7bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.b185f6a7bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:39:16.903+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-19T10:56:16.9-05:00", + "issued": "2022-12-19T10:56:16.9-05:00", + "valueQuantity": { + "value": 2126651898 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:37:09.9+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-19T07:24:09.891-05:00", + "issued": "2022-12-19T07:24:09.891-05:00", + "valueQuantity": { + "value": 941137949 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.1a5cf010bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.1a5cf010bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:35:02.83+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-19T10:44:02.819-05:00", + "issued": "2022-12-19T10:44:02.819-05:00", + "valueQuantity": { + "value": 1868889329 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Observation/1.f59dca04bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.f59dca04bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:34:40.32+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + }, + "effectiveDateTime": "2022-12-19T11:11:40.299-05:00", + "issued": "2022-12-19T11:11:40.299-05:00", + "valueQuantity": { + "value": 1577878080 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-682d4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-682d4f56bc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.682d4f56bc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:44:05.61+00:00", + "end": "2022-12-19T16:44:05.61+00:00" + }, + "recorded": "2022-12-19T16:44:05.61+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-d12b4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-d12b4f56bc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.d12b4f56bc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:44:03.04+00:00", + "end": "2022-12-19T16:44:03.04+00:00" + }, + "recorded": "2022-12-19T16:44:03.04+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-028d6f3ebc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-028d6f3ebc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.028d6f3ebc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:43:26.953+00:00", + "end": "2022-12-19T16:43:26.953+00:00" + }, + "recorded": "2022-12-19T16:43:26.953+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-54e37a32bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-54e37a32bc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.54e37a32bc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:43:10.48+00:00", + "end": "2022-12-19T16:43:10.48+00:00" + }, + "recorded": "2022-12-19T16:43:10.48+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-d24b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-d24b67f6bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.d24b67f6bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:41:23.93+00:00", + "end": "2022-12-19T16:41:23.93+00:00" + }, + "recorded": "2022-12-19T16:41:23.93+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-a94b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-a94b67f6bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.a94b67f6bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:41:22.403+00:00", + "end": "2022-12-19T16:41:22.403+00:00" + }, + "recorded": "2022-12-19T16:41:22.403+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-f1b562d2bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-f1b562d2bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.f1b562d2bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:40:27.56+00:00", + "end": "2022-12-19T16:40:27.56+00:00" + }, + "recorded": "2022-12-19T16:40:27.56+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-2d6720babb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-2d6720babb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.2d6720babb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:39:45.6+00:00", + "end": "2022-12-20T03:22:56.517+00:00" + }, + "recorded": "2022-12-20T03:22:56.517+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "UPDATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-b185f6a7bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-b185f6a7bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.b185f6a7bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:39:16.903+00:00", + "end": "2022-12-19T16:39:16.903+00:00" + }, + "recorded": "2022-12-19T16:39:16.903+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.74ec7d5fbb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:37:09.9+00:00", + "end": "2022-12-19T16:37:09.9+00:00" + }, + "recorded": "2022-12-19T16:37:09.9+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-1a5cf010bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-1a5cf010bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.1a5cf010bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:35:02.83+00:00", + "end": "2022-12-19T16:35:02.83+00:00" + }, + "recorded": "2022-12-19T16:35:02.83+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir/Provenance/17-f59dca04bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "17-f59dca04bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.f59dca04bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:34:40.32+00:00", + "end": "2022-12-19T16:34:40.32+00:00" + }, + "recorded": "2022-12-19T16:34:40.32+00:00", + "activity": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + } + ] + }, + "agent": [ + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + }, + "actor": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + }, + "actor": { + "display": "Sweetriver" + }, + "userId": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + }, + { + "role": { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + }, + "actor": { + "display": "SystemUser" + }, + "userId": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + ] + } + } + ] +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/encoding_cda.xml b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/encoding_cda.xml new file mode 100644 index 0000000..2fb6bf6 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/encoding_cda.xml @@ -0,0 +1,46 @@ + + + + + + + + Minute Clinic Continuity of Care Document + + + + + + + + + + + George + Example + + + + + + + +
+ + + + Progress Notes + + + + War, Jo - 07/31/2007 9:02 AM EST + Smoking Status � Never Smoker Smokeless Tobacco � Not on file + + + +
+
+
+
+
\ No newline at end of file diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/hl7.txt b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/hl7.txt new file mode 100644 index 0000000..ffc2fe4 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/hl7.txt @@ -0,0 +1,22 @@ +MSH|^~\&|LAB|MYFAC|LAB||201411130917||ORU^R01|3216598|D|2.3|||AL|NE| +PID|1|ABC123DF|AND234DA_PID3|PID_4_ALTID|Smith^Patient^M||19670202 |F|||2222 22 st^^LAKE COUNTRY^NY^22222||222-222-2222|||||7890| +PV1|1|O|MYFACSOMPL||||^Smith^Patient^^^^^XAVS|||||||||||REF||SELF|||||||||||||||||||MYFAC||REG|||201411071440||||||||23390^PV1_52Smith^PV1_52Patient^H^^Dr^^PV1_52Mnemonic| +ORC|RE|PT103933301.0100|||CM|N|||201411130917|^John^Doctor^J.^^^^KYLA||^Smith^Patient^^^^^XAVS|MYFAC| +OBR|1|PT1311:H00001R301.0100|PT1311:H00001R|301.0100^Complete Blood Count (CBC)^00065227^57021-8^CBC \T\ Auto Differential^pCLOCD|R||201411130914|||KYLA||||201411130914||^Smith^Patient^^^^^XAVS||00065227||||201411130915||LAB|F||^^^^^R|^Smith^Patient^^^^^XAVS| +OBX|1|NM|301.0500^White Blood Count (WBC)^00065227^6690-2^Leukocytes^pCLOCD|1|10.1|10\S\9/L|3.1-9.7|H||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|2|NM|301.0600^Red Blood Count (RBC)^00065227^789-8^Erythrocytes^pCLOCD|1|3.2|10\S +/L|3.7-5.0|L||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|3|NM|301.0700^Hemoglobin (HGB)^00065227^718-7^Hemoglobin^pCLOCD|1|140|g/L|118-151|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|4|NM|301.0900^Hematocrit (HCT)^00065227^4544-3^Hematocrit^pCLOCD|1|0.34|L/L|0.33-0.45|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|5|NM|301.1100^MCV^00065227^787-2^Mean Corpuscular Volume^pCLOCD|1|98.0|fL|84.0-98.0|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|6|NM|301.1300^MCH^00065227^785-6^Mean Corpuscular Hemoglobin^pCLOCD|1|27.0|pg|28.3-33.5|L||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|7|NM|301.1500^MCHC^00065227^786-4^Mean Corpuscular Hemoglobin Concentration^pCLOCD|1|330|g/L|329-352|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|8|NM|301.1700^RDW^00065227^788-0^Erythrocyte Distribution Width^pCLOCD|1|12.0|%|12.0-15.0|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|9|NM|301.1900^Platelets^00065227^777-3^Platelets^pCLOCD|1|125|10\S\9/L|147-375|L||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|10|NM|301.2100^Neutrophils^00065227^751-8^Neutrophils^pCLOCD|1|8.0|10\S\9/L|1.2-6.0|H||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|11|NM|301.2300^Lymphocytes^00065227^731-0^Lymphocytes^pCLOCD|1|1.0|10\S\9/L|0.6-3.1|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|12|NM|301.2500^Monocytes^00065227^742-7^Monocytes^pCLOCD|1|1.0|10\S\9/L|0.1-0.9|H||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|13|NM|301.2700^Eosinophils^00065227^711-2^Eosinophils^pCLOCD|1|0.0|10\S\9/L|0.0-0.5|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +OBX|14|NM|301.2900^Basophils^00065227^704-7^Basophils^pCLOCD|1|0.0|10\S\9/L|0.0-0.2|N||A~S|F|||201411130916|MYFAC^MyFake Hospital^L| +ZDR||^Smith^Patient^^^^^XAVS^^^^^XX^^ATP| +ZPR|| diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/nemsis_bundle.json b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/nemsis_bundle.json new file mode 100644 index 0000000..4b99640 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/nemsis_bundle.json @@ -0,0 +1,3236 @@ +{ + "resourceType": "Bundle", + "type": "batch-response", + "entry": [ + { + "fullUrl": "https://api.rosetta.careevolution.com/Patient/35b77437-425d-419c-90b5-af4bc433ebe9", + "resource": { + "resourceType": "Patient", + "id": "35b77437-425d-419c-90b5-af4bc433ebe9", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "IheTestPatient" + }, + { + "system": "http://rosetta.careevolution.com/identifiers/Proprietary/1.3.6.1.4.1.37608", + "value": "IheTestPatient" + } + ], + "name": [ + { + "use": "official", + "family": "Smith", + "given": [ + "Patient" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "534-555-6666", + "use": "home" + } + ], + "gender": "female", + "birthDate": "1956-08-13", + "deceasedBoolean": false, + "address": [ + { + "use": "home", + "line": [ + "34 Drury Lane" + ], + "city": "Disney Land", + "state": "CA", + "postalCode": "90210" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/AllergyIntolerance/7e586cfb-0c3c-43ae-ac94-acfb26b28c91", + "resource": { + "resourceType": "AllergyIntolerance", + "id": "7e586cfb-0c3c-43ae-ac94-acfb26b28c91", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-allergyintolerance|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", + "code": "active" + }, + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/AllergyClinicalStatus", + "code": "active", + "display": "active", + "userSelected": true + } + ] + }, + "category": [ + "environment" + ], + "criticality": "high", + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "372687004", + "display": "Amoxicillin", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "372687004", + "display": "Amoxicillin (substance)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4156860", + "display": "Amoxicillin", + "userSelected": false + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "723", + "display": "amoxicillin", + "userSelected": false + } + ] + }, + "patient": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "onsetDateTime": "2008-03-10T00:00:00-04:00", + "recordedDate": "2008-03-10T00:00:00-04:00", + "reaction": [ + { + "manifestation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.CareEvolution/AllergyReaction", + "code": "Rash", + "display": "Rash", + "userSelected": true + } + ] + } + ], + "description": "Rash" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Condition/5.4a6057fb70a64ca291b38ef98c5a7382", + "resource": { + "resourceType": "Condition", + "id": "5.4a6057fb70a64ca291b38ef98c5a7382", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", + "code": "active" + }, + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/DiagnosisStatus", + "code": "active", + "display": "active", + "userSelected": true + } + ] + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", + "code": "unconfirmed" + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "55607006", + "display": "Problem", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "55607006", + "display": "Problem (finding)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4206460", + "display": "Problem", + "userSelected": false + } + ] + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.103", + "code": "0261", + "display": "Streptobacillary fever", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/sid/icd-9-cm/diagnosis", + "code": "026.1", + "display": "Streptobacillary fever", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "44833220", + "display": "Streptobacillary fever", + "userSelected": false + }, + { + "system": "http://snomed.info/sct", + "code": "52138004", + "display": "Streptobacillary fever (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "A25.1", + "display": "Streptobacillosis", + "userSelected": false + } + ], + "text": "Streptobacillary fever" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "onsetDateTime": "2009-06-07T18:00:00-04:00" + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Condition/5.41e24cc8e2e44dadaa5c1b2b6d746dad", + "resource": { + "resourceType": "Condition", + "id": "5.41e24cc8e2e44dadaa5c1b2b6d746dad", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", + "code": "unconfirmed" + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-category", + "code": "encounter-diagnosis" + }, + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/DiagnosisType", + "code": "EncounterReason", + "display": "Encounter Reason", + "userSelected": true + } + ] + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.103", + "code": "0261", + "display": "Streptobacillary fever", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/sid/icd-9-cm/diagnosis", + "code": "026.1", + "display": "Streptobacillary fever", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "44833220", + "display": "Streptobacillary fever", + "userSelected": false + }, + { + "system": "http://snomed.info/sct", + "code": "52138004", + "display": "Streptobacillary fever (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "A25.1", + "display": "Streptobacillosis", + "userSelected": false + } + ], + "text": "Streptobacillary fever" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "encounter": { + "reference": "Encounter/1bdc26a5-324a-4190-a403-b63067da1c19" + }, + "onsetDateTime": "2009-06-07T18:00:00-04:00" + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/DiagnosticReport/4.708468efc4924839b3cddb3ab5775c1f", + "resource": { + "resourceType": "DiagnosticReport", + "id": "4.708468efc4924839b3cddb3ab5775c1f", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "value": "16631200720090609061700" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB" + } + ], + "text": "LAB" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "166312007", + "display": "CHEM24S", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "166312007", + "display": "Blood chemistry (procedure)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4014134", + "display": "Blood chemistry", + "userSelected": false + } + ], + "text": "CHEM24S" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-09T18:17:00-04:00", + "issued": "2009-06-09T18:17:00-04:00", + "result": [ + { + "reference": "Observation/2.898b46638a8a4f0f8ad5faeca51f381b" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/DiagnosticReport/4.9a8ce5a0c44d4affb569cf64ed82dbb2", + "resource": { + "resourceType": "DiagnosticReport", + "id": "4.9a8ce5a0c44d4affb569cf64ed82dbb2", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "value": "2660400720090608120900" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB" + } + ], + "text": "LAB" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "26604007", + "display": "CBC", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "26604007", + "display": "Complete blood count (procedure)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4132152", + "display": "Complete blood count", + "userSelected": false + } + ], + "text": "CBC" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "result": [ + { + "reference": "Observation/2.82f0c47ab5274b738bd8bcd178595ed1" + }, + { + "reference": "Observation/2.862f583179e347038c75895ceac08750" + }, + { + "reference": "Observation/2.3a6b507900634284a6c8edf7a974e65d" + }, + { + "reference": "Observation/2.48172d29edee4125ac8f975e7c6f9cf8" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/DiagnosticReport/4.19f34e64dac6462fa2e86113eacd32c8", + "resource": { + "resourceType": "DiagnosticReport", + "id": "4.19f34e64dac6462fa2e86113eacd32c8", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "value": "Operative20090607050000" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB" + } + ], + "text": "LAB" + } + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/LabService", + "code": "Operative", + "display": "Operative", + "userSelected": true + } + ], + "text": "Operative" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-07T17:00:00-04:00", + "issued": "2009-06-07T17:00:00-04:00", + "result": [ + { + "reference": "Observation/2.a346355cd2524a6686aeecd35e7f4aab" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/DiagnosticReport/4.822796b740bb446791ef4e77d0756f42", + "resource": { + "resourceType": "DiagnosticReport", + "id": "4.822796b740bb446791ef4e77d0756f42", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "value": "7930100820090608120900" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB" + } + ], + "text": "LAB" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "79301008", + "display": "Electrolytes measurement (procedure)", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "79301008", + "display": "Electrolytes measurement (procedure)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4193783", + "display": "Electrolytes measurement", + "userSelected": false + } + ], + "text": "Electrolytes measurement (procedure)" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "result": [ + { + "reference": "Observation/2.1d376f381e404f629a4b668eb4bf7d72" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Encounter/1bdc26a5-324a-4190-a403-b63067da1c19", + "resource": { + "resourceType": "Encounter", + "id": "1bdc26a5-324a-4190-a403-b63067da1c19", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "system": "http://rosetta.careevolution.com/encounteridentifiers", + "value": "07f8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "in-progress", + "class": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#coding", + "valueCoding": { + "system": "http://rosetta.careevolution.com/codes/Proprietary.CareEvolution/PatientClass", + "code": "Inpatient", + "display": "Inpatient", + "userSelected": true + } + }, + { + "url": "http://careevolution.com/fhirextensions#coding", + "valueCoding": { + "system": "https://athena.ohdsi.org/", + "code": "9201", + "display": "Inpatient Visit", + "userSelected": false + } + } + ], + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "IMP" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "participant": [ + { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#dataSource", + "extension": [ + { + "url": "code", + "valueString": "cj51SkDNLEqvzVeIR5m1jQ==" + }, + { + "url": "name", + "valueString": "Custodian: Community Health Information Exchange;Author: Community Health Information Exchange;" + } + ] + } + ], + "type": [ + { + "coding": [ + { + "system": "http://careevolution.com/fhircodes#CaregiverRelationshipType", + "code": "ScheduledCaregiver", + "display": "Scheduled Caregiver", + "userSelected": true + } + ] + } + ], + "period": { + "start": "2004-05-06T00:00:00-04:00" + }, + "individual": { + "reference": "Practitioner/a0ba6517-af46-44e6-8359-d7297d27a763" + } + } + ], + "period": { + "start": "2009-06-07T17:00:00-04:00" + }, + "diagnosis": [ + { + "condition": { + "reference": "Condition/5.41e24cc8e2e44dadaa5c1b2b6d746dad" + }, + "use": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/diagnosis-role", + "code": "CC" + }, + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/DiagnosisType", + "code": "EncounterReason", + "display": "Encounter Reason", + "userSelected": true + } + ] + } + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Encounter/3cc0c5b3-5e61-4c2d-b2f5-6d3a77b438d2", + "resource": { + "resourceType": "Encounter", + "id": "3cc0c5b3-5e61-4c2d-b2f5-6d3a77b438d2", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "system": "http://rosetta.careevolution.com/encounteridentifiers", + "value": "08f8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "in-progress", + "class": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#coding", + "valueCoding": { + "system": "http://rosetta.careevolution.com/codes/Proprietary.CareEvolution/PatientClass", + "code": "PCPVisit", + "display": "PCP Visit", + "userSelected": true + } + }, + { + "url": "http://careevolution.com/fhirextensions#coding", + "valueCoding": { + "system": "https://athena.ohdsi.org/", + "code": "9202", + "display": "Outpatient Visit", + "userSelected": false + } + } + ], + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "AMB" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "period": { + "start": "2009-08-10T22:00:00-04:00" + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Practitioner/a0ba6517-af46-44e6-8359-d7297d27a763", + "resource": { + "resourceType": "Practitioner", + "id": "a0ba6517-af46-44e6-8359-d7297d27a763", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner|3.1.1", + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Practitioner|2.0" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_CareEvolution" + }, + "identifier": [ + { + "use": "usual", + "system": "http://rosetta.careevolution.com/identifiers/CareEvolution/CaregiverIdentifier/1.3.6.1.4.1.37608_CareEvolution", + "value": "7v1bjE+iv1TpK7xwyhaXbw==" + }, + { + "system": "http://rosetta.careevolution.com/identifiers/Proprietary/1.3.6.1.4.1.37608", + "value": "c68394cb-57ad-e411-8260-0050b664cec5" + } + ], + "name": [ + { + "use": "official", + "family": "Smith", + "given": [ + "David", + "K" + ], + "suffix": [ + "MD" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "212-555-7351", + "use": "home" + } + ], + "address": [ + { + "use": "home", + "line": [ + "543 Doctor Avenue" + ], + "city": "New York", + "state": "NY", + "postalCode": "10001" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationAdministration/d07d711c-d897-410a-a3da-d605e5ed1a58", + "resource": { + "resourceType": "MedicationAdministration", + "id": "d07d711c-d897-410a-a3da-d605e5ed1a58", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#medicationAdministration-scheduledTime", + "valueDateTime": "2009-08-11T22:00:00-04:00" + } + ], + "identifier": [ + { + "use": "usual", + "value": "1cf8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "medicationCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.88", + "code": "C0981193", + "display": "Acetaminophen 325 mg oral tablet", + "userSelected": true + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "313782", + "display": "acetaminophen 325 MG Oral Tablet", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "1127433", + "display": "acetaminophen 325 MG Oral Tablet", + "userSelected": false + } + ], + "text": "Acetaminophen 325 mg oral tablet" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectivePeriod": { + "start": "2009-08-11T22:00:00-04:00", + "end": "2009-08-11T22:00:00-04:00" + }, + "request": { + "reference": "MedicationRequest/7f7348c8-7377-41ba-9628-5a68aa34ceec" + }, + "dosage": { + "dose": { + "value": -1, + "unit": "milligram", + "system": "http://unitsofmeasure.org", + "code": "mg" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationAdministration/b5def510-50f4-4efc-988c-a7e6e74ef61c", + "resource": { + "resourceType": "MedicationAdministration", + "id": "b5def510-50f4-4efc-988c-a7e6e74ef61c", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#medicationAdministration-scheduledTime", + "valueDateTime": "2009-08-11T17:00:00-04:00" + } + ], + "identifier": [ + { + "use": "usual", + "value": "1df8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "medicationCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.88", + "code": "C0973886", + "display": "Albuterol 0.09mg/actuat inhalant solution", + "userSelected": true + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "329498", + "display": "albuterol 0.09 MG/ACTUAT", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "1154459", + "display": "albuterol 0.09 MG/ACTUAT", + "userSelected": false + } + ], + "text": "Albuterol 0.09mg/actuat inhalant solution" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectivePeriod": { + "start": "2009-08-11T17:00:00-04:00", + "end": "2009-08-11T17:00:00-04:00" + }, + "request": { + "reference": "MedicationRequest/90a1faa3-43f8-4565-b170-cc4025cab2db" + }, + "dosage": { + "route": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.5.112", + "code": "PO", + "display": "PO", + "userSelected": true + } + ] + }, + "dose": { + "value": -1, + "unit": "milligram", + "system": "http://unitsofmeasure.org", + "code": "mg" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationAdministration/e33653e5-387a-498e-acde-14cce4ddac78", + "resource": { + "resourceType": "MedicationAdministration", + "id": "e33653e5-387a-498e-acde-14cce4ddac78", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#medicationAdministration-scheduledTime", + "valueDateTime": "2009-08-12T17:00:00-04:00" + } + ], + "identifier": [ + { + "use": "usual", + "value": "1ef8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "medicationCodeableConcept": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.CareEvolution/MedicationProfileMedication", + "code": "C3243", + "display": "Saline", + "userSelected": true + } + ], + "text": "Saline" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectivePeriod": { + "start": "2009-08-12T17:00:00-04:00", + "end": "2009-08-12T17:00:00-04:00" + }, + "request": { + "reference": "MedicationRequest/1d1e84cd-5b15-4123-b21a-3125ef6ad192" + }, + "dosage": { + "route": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.2.16.840.1.113883.3.26.1.1/MedicationAdministrationRoute", + "code": "C38216", + "display": "Respiratory (Inhalation)", + "userSelected": true + } + ] + }, + "dose": { + "value": -1, + "unit": "milliliter", + "system": "http://unitsofmeasure.org", + "code": "mL" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationRequest/90a1faa3-43f8-4565-b170-cc4025cab2db", + "resource": { + "resourceType": "MedicationRequest", + "id": "90a1faa3-43f8-4565-b170-cc4025cab2db", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PLAC" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "1df8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "intent": "order", + "reportedBoolean": false, + "medicationCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.88", + "code": "C0973886", + "display": "Albuterol 0.09mg/actuat inhalant solution", + "userSelected": true + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "329498", + "display": "albuterol 0.09 MG/ACTUAT", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "1154459", + "display": "albuterol 0.09 MG/ACTUAT", + "userSelected": false + } + ], + "text": "Albuterol 0.09mg/actuat inhalant solution" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "authoredOn": "2009-08-11T17:00:00-04:00", + "requester": { + "reference": "Practitioner/a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd" + }, + "dosageInstruction": [ + { + "text": "Albuterol 0.09mg/actuat inhalant solution", + "timing": { + "event": [ + "2009-08-11T17:00:00-04:00" + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/OrderFrequency", + "code": "Unspecified", + "display": "Unspecified", + "userSelected": true + } + ] + } + }, + "asNeededBoolean": false, + "route": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.5.112", + "code": "PO", + "display": "PO", + "userSelected": true + } + ] + }, + "doseAndRate": [ + { + "doseRange": { + "low": { + "value": 5.5, + "unit": "milligram", + "system": "http://unitsofmeasure.org", + "code": "mg" + }, + "high": { + "value": 5.5, + "unit": "milligram", + "system": "http://unitsofmeasure.org", + "code": "mg" + } + } + } + ] + } + ], + "dispenseRequest": { + "validityPeriod": { + "start": "2009-08-11T17:00:00-04:00", + "end": "2009-08-11T17:00:00-04:00" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationRequest/72c3742d-ec08-489f-906a-bd039a8c0d07", + "resource": { + "resourceType": "MedicationRequest", + "id": "72c3742d-ec08-489f-906a-bd039a8c0d07", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PLAC" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "2ef8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "intent": "order", + "reportedBoolean": false, + "medicationCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.88", + "code": "197454", + "display": "Cephalexin 500 MG oral tablet", + "userSelected": true + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "197454", + "display": "cephalexin 500 MG Oral Tablet", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "1786652", + "display": "cephalexin 500 MG Oral Tablet", + "userSelected": false + } + ], + "text": "Cephalexin 500 MG oral tablet" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "authoredOn": "2009-06-10T16:00:00-04:00", + "requester": { + "reference": "Practitioner/a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd" + }, + "dosageInstruction": [ + { + "text": "Cephalexin 500 MG oral tablet", + "timing": { + "event": [ + "2009-06-10T16:00:00-04:00" + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/OrderFrequency", + "code": "Unspecified", + "display": "Unspecified", + "userSelected": true + } + ] + } + }, + "asNeededBoolean": false + } + ], + "dispenseRequest": { + "validityPeriod": { + "start": "2009-06-10T16:00:00-04:00" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationRequest/44ad877e-e2de-45bd-8981-b13b3de29458", + "resource": { + "resourceType": "MedicationRequest", + "id": "44ad877e-e2de-45bd-8981-b13b3de29458", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PLAC" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "2df8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "intent": "order", + "reportedBoolean": false, + "medicationCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.88", + "code": "309362", + "display": "Clopidogrel 75 MG oral tablet", + "userSelected": true + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "309362", + "display": "clopidogrel 75 MG Oral Tablet", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "19075601", + "display": "clopidogrel 75 MG Oral Tablet", + "userSelected": false + } + ], + "text": "Clopidogrel 75 MG oral tablet" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "authoredOn": "2009-08-15T09:00:00-04:00", + "requester": { + "reference": "Practitioner/a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd" + }, + "dosageInstruction": [ + { + "text": "Clopidogrel 75 MG oral tablet", + "timing": { + "event": [ + "2009-08-15T09:00:00-04:00" + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/OrderFrequency", + "code": "Unspecified", + "display": "Unspecified", + "userSelected": true + } + ] + } + }, + "asNeededBoolean": false + } + ], + "dispenseRequest": { + "validityPeriod": { + "start": "2009-08-15T09:00:00-04:00" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationRequest/7f7348c8-7377-41ba-9628-5a68aa34ceec", + "resource": { + "resourceType": "MedicationRequest", + "id": "7f7348c8-7377-41ba-9628-5a68aa34ceec", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PLAC" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "1cf8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "intent": "order", + "reportedBoolean": false, + "medicationCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.88", + "code": "C0981193", + "display": "Acetaminophen 325 mg oral tablet", + "userSelected": true + }, + { + "system": "http://www.nlm.nih.gov/research/umls/rxnorm", + "code": "313782", + "display": "acetaminophen 325 MG Oral Tablet", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "1127433", + "display": "acetaminophen 325 MG Oral Tablet", + "userSelected": false + } + ], + "text": "Acetaminophen 325 mg oral tablet" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "authoredOn": "2009-08-11T22:00:00-04:00", + "requester": { + "reference": "Practitioner/a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd" + }, + "dosageInstruction": [ + { + "text": "Acetaminophen 325 mg oral tablet", + "timing": { + "event": [ + "2009-08-11T22:00:00-04:00" + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/OrderFrequency", + "code": "Unspecified", + "display": "Unspecified", + "userSelected": true + } + ] + } + }, + "asNeededBoolean": false, + "doseAndRate": [ + { + "doseRange": { + "low": { + "value": 325, + "unit": "milligram", + "system": "http://unitsofmeasure.org", + "code": "mg" + }, + "high": { + "value": 325, + "unit": "milligram", + "system": "http://unitsofmeasure.org", + "code": "mg" + } + } + } + ] + } + ], + "dispenseRequest": { + "validityPeriod": { + "start": "2009-08-11T22:00:00-04:00", + "end": "2009-08-11T22:00:00-04:00" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/MedicationRequest/1d1e84cd-5b15-4123-b21a-3125ef6ad192", + "resource": { + "resourceType": "MedicationRequest", + "id": "1d1e84cd-5b15-4123-b21a-3125ef6ad192", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "PLAC" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "1ef8cd8b-58ad-e411-8260-0050b664cec5" + } + ], + "status": "completed", + "intent": "order", + "reportedBoolean": false, + "medicationCodeableConcept": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.CareEvolution/OrderableItem", + "code": "C3243", + "display": "Saline", + "userSelected": true + } + ], + "text": "Saline" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "authoredOn": "2009-08-12T17:00:00-04:00", + "requester": { + "reference": "Practitioner/a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd" + }, + "dosageInstruction": [ + { + "text": "Saline", + "timing": { + "event": [ + "2009-08-12T17:00:00-04:00" + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/OrderFrequency", + "code": "Unspecified", + "display": "Unspecified", + "userSelected": true + } + ] + } + }, + "asNeededBoolean": false, + "route": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.2.16.840.1.113883.3.26.1.1/OrderRoute", + "code": "C38216", + "display": "Respiratory (Inhalation)", + "userSelected": true + } + ] + }, + "doseAndRate": [ + { + "doseRange": { + "low": { + "value": 0.5, + "unit": "milliliter", + "system": "http://unitsofmeasure.org", + "code": "mL" + }, + "high": { + "value": 0.5, + "unit": "milliliter", + "system": "http://unitsofmeasure.org", + "code": "mL" + } + } + } + ] + } + ], + "dispenseRequest": { + "validityPeriod": { + "start": "2009-08-12T17:00:00-04:00", + "end": "2009-08-12T17:00:00-04:00" + } + } + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Practitioner/a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd", + "resource": { + "resourceType": "Practitioner", + "id": "a75b0d51-7464-4e9a-9c6c-d3a7488fd2bd", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner|3.1.1", + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Practitioner|2.0" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_CareEvolution" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#practitioner-role", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary/CaregiverType", + "code": "Prescriber", + "display": "Prescriber", + "userSelected": true + } + ] + } + } + ], + "identifier": [ + { + "use": "usual", + "system": "http://rosetta.careevolution.com/identifiers/CareEvolution/CaregiverIdentifier/1.3.6.1.4.1.37608_CareEvolution", + "value": "Unknown" + } + ], + "name": [ + { + "use": "official", + "family": "Unknown" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.898b46638a8a4f0f8ad5faeca51f381b", + "resource": { + "resourceType": "Observation", + "id": "2.898b46638a8a4f0f8ad5faeca51f381b", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.708468efc4924839b3cddb3ab5775c1f" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "275790008", + "display": "CHLORIDE", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "275790008", + "display": "Chloride in sample (finding)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4077512", + "display": "Chloride in sample", + "userSelected": false + } + ], + "text": "CHLORIDE" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-09T18:17:00-04:00", + "issued": "2009-06-09T18:17:00-04:00", + "valueString": "112 MMOL/L", + "interpretation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/Acuity", + "code": "H", + "display": "H", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/v3/ObservationInterpretation", + "code": "H", + "display": "High", + "userSelected": false + } + ] + } + ], + "referenceRange": [ + { + "low": { + "value": 98, + "unit": "MMOL/L" + }, + "high": { + "value": 107, + "unit": "MMOL/L" + }, + "text": "98-107 MMOL/L" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.3a6b507900634284a6c8edf7a974e65d", + "resource": { + "resourceType": "Observation", + "id": "2.3a6b507900634284a6c8edf7a974e65d", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.9a8ce5a0c44d4affb569cf64ed82dbb2" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "16378004", + "display": "PLT", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "16378004", + "display": "Platelet (cell structure)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4039428", + "display": "Platelet", + "userSelected": false + } + ], + "text": "PLT" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "valueString": "132 K/CUMM", + "interpretation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/Acuity", + "code": "L", + "display": "L", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/v3/ObservationInterpretation", + "code": "L", + "display": "Low", + "userSelected": false + } + ] + } + ], + "referenceRange": [ + { + "low": { + "value": 150, + "unit": "K/CUMM" + }, + "high": { + "value": 450, + "unit": "K/CUMM" + }, + "text": "150-450 K/CUMM" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.a346355cd2524a6686aeecd35e7f4aab", + "resource": { + "resourceType": "Observation", + "id": "2.a346355cd2524a6686aeecd35e7f4aab", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.19f34e64dac6462fa2e86113eacd32c8" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/LabObservationType", + "code": "Operative", + "display": "Operative", + "userSelected": true + } + ], + "text": "OPERATIVE NOTE\nDICTATED BY: Jack Blue, MD\nDICTATED: 6/7/2009 8:03 P 000087489\nTRANSCRIBED: 6/8/2009 1:06 A vl D\nPATIENT NAME: Test, Ihe\nHEALTH RECORD NO.: 123456783\nBILLING NO.: 88888888\nROOM N" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-07T17:00:00-04:00", + "issued": "2009-06-07T17:00:00-04:00", + "valueString": "OPERATIVE NOTE\nDICTATED BY: Jack Blue, MD\nDICTATED: 6/7/2009 8:03 P 000087489\nTRANSCRIBED: 6/8/2009 1:06 A vl D\nPATIENT NAME: Test, Ihe\nHEALTH RECORD NO.: 123456783\nBILLING NO.: 88888888\nROOM NO.: SIC\nDate of Procedure: 11/28/06\nPREOPERATIVE DIAGNOSES: Aortic stenosis and mitral\ninsufficiency.\nPOSTOPERATIVE DIAGNOSES: Aortic stenosis and mitral\ninsufficiency.\nPROCEDURE PERFORMED: Aortic valve replacement with a 21 mm\nEdwards Magna bovine pericardial bioprosthesis and a reverse\nsaphenous vein graft to the right coronary.\nSURGEON: Dr. Blue.\nASSISTANT: Pat Yellow, CST.\nANESTHESIA: General tracheal anesthesia, Dr. Green.\nAortic cross clamp time was 117 minutes.\nCardiopulmonary bypass time was 168 minutes.\nAtrial and ventricular pacing wires.\nDrains - 32 French mediastinal chest tube, 28 French\nmediastinal chest tube.\nFINDINGS: Good saphenous vein conduit, normal ascending aorta,\naortic valve was trileaflet with heavy calcification to the\nleaflets. The annulus was not too heavily calcified with\nmoderate aortic annular calcification. Also noted was a right\ncoronary ostial intimal flap that was noted after I was\ngetting ready to seat the valve and this will be described in\nthe text of the operation; therefore the right coronary was\ngrafted. The right coronary was a 2.5 mm vessel with no\ndistal disease.\nINDICATIONS FOR PROCEDURE: The patient is a 73-year-old woman\nwho was found to have known aortic stenosis and found to have\nbreast carcinoma needing resection of her breast cancer;\nhowever, has had severe aortic stenosis prior to undergoing\nbreast cancer surgery. She needed her heart fixed. The\npatient is brought to the operating room for aortic valve\nreplacement. Preoperative echocardiogram demonstrates severe\naortic stenosis and moderate aortic insufficiency and 2+\nmitral insufficiency. Intraoperative transesophageal\nechocardiogram was performed by Dr. Mauve.\nDESCRIPTION OF THE PROCEDURE: The patient was brought to the\noperating room and placed in a comfortable supine position on\nthe operating table. She underwent induction of general\nendotracheal anesthesia and placement of invasive monitor,\nlines and intraoperative echocardiogram by Dr. Mauve.\nEchocardiogram demonstrated severe aortic stenosis and heavily\ncalcified leaflets with moderate mitral insufficiency given\nher age. It was my impression that decompressing her outflow\ntract would improve her mitral valve insufficiency. The\npatient was then positioned, prepped and draped in the usual\nsterile fashion. A median sternotomy was accomplished. The\nchest was opened and a mediastinal dissection proceeded. The\npericardium was opened and suspended. The patient was\nheparinized. The distal ascending aorta was cannulated\nthrough double aortic pursestring sutures. A two stage venous\ncannula was positioned to the right atrial appendage\npursestring and the patient was initiated on cardiopulmonary\nbypass. Antegrade and retrograde cardioplegia cannulas were\npositioned. An LV sump cannula was positioned through right\nsuperior pulmonary vein. The patient was cooled to 32 degrees\ncentigrade. The aorta was cross clamped and antegrade and\nretrograde cold blood cardioplegia was administered. After\ndiastolic rest of the heart we made a transverse aortotomy\nwith a hockey stick extension down into the noncoronary sinus.\nExposure of the valve was actually pretty descent. Valve\nfindings as noted previously. The valve was sharply excised.\nThe majority of the annular debridement was done fortunately\nwith the scissors; i.e., the majority of the annular\ncalcifications were in the leaflets and not so much in the\nannulus. Once we got the leaflets cut out the annulus was d\nbrided of the remainder of the calcific debris. I incised it\nprior to placement of sutures and it sized out to 21 mm sized\nout very easily and in fact a 23 almost fit. I then placed\nmultiple annular sutures circumferentially about the annulus\nthat was consistent with interrupted 2-0 Ethibond horizontal\nmattress pledgeted sutures with pledgets on the ventricular\nside. Once we got all our sutures in, I resized the 21 mm\nsizer. A 21 interannular sizer was quit snug; however, the\nsizer on the supra-annular position looked quite comfortable.\nA 21 mm valve was brought up to the field and it was washed\nand then brought to operative field. While I was examining\nthe root of the heart I noted that there an intimal dissection\nof the ostium of the right coronary that is completely\nunexplainable. There was no direct cannulation of the right\ncoronary ostium. There was no mechanical debridement at the\nright coronary ostium but fortuitously identified the right\ncoronary intimal tear and it was an intimal tear of the aortic\nwall and it extended to the right coronary ostium. I tacked\nthis intima down to the aortic wall with interrupted\nhorizontal mattress 5-0 Prolene suture but I was very\nuncomfortable just simply leaving this in place that it posed\nthe possibility for acute right coronary dissection, RV\ninfarct, and inferior wall infarct postoperatively. Therefore,\neven though she did not have any coronary disease, I was very\nconcerned about leaving her with an unprotected right\ncoronary. I therefore asked Pat Yellow, my assistant,\nto scrub and take a segment of vein from the left lower leg\ngreater saphenous vein was harvested and the wound was closed\nin layers with absorbable suture. While she was taking vein,\nI placed annular sutures through the valve, sewing ring the\nvalve and then seated down into the annulus and it seated\nnicely. It was seated first in the left coronary sinus\nfollowed by the right coronary sinus followed by the\nnoncoronary sinus. The remainder of the sutures was tied\ncircumferentially. I examined the valve and it appeared to be\nseated well and I was very pleased with the seating.\nPat Yellow was still procuring vein when I was closing\nthe aortotomy. The aortotomy was closed in a two layer\nfashion with running 4-0 Prolene in a two layer fashion. The\nfirst layer was a running horizontal mattress with 4-0 Prolene\nfollowed by over and over 4-0 Prolene. By this time the vein\nwas out. I identified a spot in the mid right coronary about\nthe level of the acute margin, made an arteriotomy and sewed\nthe vein graft end-to-side with running 7-0 Prolene suture. I\nmade a single 4.4 mm aortotomy distal to my transverse\naortotomy and anastomosed the vein graft end-to-side with\nrunning 5-0 Prolene suture. The patient was rewarmed while I\nreconstructed the last anastomosis and a warm dose of\ncardioplegia was given through the retrograde cannula. The\npatient was positioned in Trendelenburg position. The\nventricle and aorta were de-aired out the ascending aorta.\nThe patient was positioned head down position. The aortic\ncross clamp was removed. The patient's rhythm returned to\nventricular fibrillation. I defibrillated the heart several\ntimes with loading dose of Lidocaine and amiodarone and was\nfinally able to get her defibrillated to a paced rhythm. The\ngraft was de-aired and the bulldog was released. The\naortotomy was hemostatic. Atrial and ventricular pacing wires\nwere positioned and mediastinal drains were positioned and\nproximal graft markers were positioned. With satisfactory\nreperfusion time, we went ahead and ventilated the patient,\nremoved the retrograde cannula was previously removed. The LV\nsump cannula was removed once I had the heart beating. We\nthen proceeded with echocardiogram interrogation.\nEchocardiogram interrogation demonstrated satisfactory\nde-airing and satisfactory valve function with no aortic\ninsufficiency and only trivial mitral insufficiency. With\nthat result, we removed the antegrade cardioplegia needle and\nthen weaned and separated from cardiopulmonary bypass on low\ndose inotropic support. The venous cannula was removed. The\npump line was administered through the aortic cannula with\nsatisfactory hemodynamics. Protamine was administered. We\ngained hemostasis through the mediastinum. The aorta was\nde-cannulated. Pursestring sutures were secured. With\nsatisfactory hemostasis we preceded closure. The sternum was\napproximated with interrupted #7 stainless steel wires. The\ndeep fascia was closed with running #1 PDS. The subcutaneous\ntissue was closed with 2-0 Vicryl. The skin edges were closed\nwith 3-0 Monocryl. The patient's wounds were dressed in a\ndry, sterile dressing. The patient tolerated the procedure\nwell with no operative complications. The patient was\nreturned to the surgical intensive care unit in critical\ncondition.\n___________________________\nJack Blue, MD\ncc: Donald M Red, MD\n Jack Blue, MD\n Chip Orange, MD\nDOCUMENT #: 2487825", + "referenceRange": [ + { + "text": "unknown" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.82f0c47ab5274b738bd8bcd178595ed1", + "resource": { + "resourceType": "Observation", + "id": "2.82f0c47ab5274b738bd8bcd178595ed1", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.9a8ce5a0c44d4affb569cf64ed82dbb2" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "767002", + "display": "WBC", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "767002", + "display": "White blood cell count (procedure)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4298431", + "display": "White blood cell count", + "userSelected": false + } + ], + "text": "WBC" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "valueString": "16.6 K/CUMM", + "interpretation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/Acuity", + "code": "H", + "display": "H", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/v3/ObservationInterpretation", + "code": "H", + "display": "High", + "userSelected": false + } + ] + } + ], + "referenceRange": [ + { + "low": { + "value": 4, + "unit": "K/CUMM" + }, + "high": { + "value": 10.5, + "unit": "K/CUMM" + }, + "text": "4.0-10.5 K/CUMM" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.48172d29edee4125ac8f975e7c6f9cf8", + "resource": { + "resourceType": "Observation", + "id": "2.48172d29edee4125ac8f975e7c6f9cf8", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.9a8ce5a0c44d4affb569cf64ed82dbb2" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "28317006", + "display": "HCT", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "28317006", + "display": "Hematocrit determination (procedure)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4151358", + "display": "Hematocrit determination", + "userSelected": false + } + ], + "text": "HCT" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "valueString": "28.6 %", + "interpretation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/Acuity", + "code": "L", + "display": "L", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/v3/ObservationInterpretation", + "code": "L", + "display": "Low", + "userSelected": false + } + ] + } + ], + "referenceRange": [ + { + "low": { + "value": 37, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "high": { + "value": 47, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "text": "37.0-47.0 %" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.862f583179e347038c75895ceac08750", + "resource": { + "resourceType": "Observation", + "id": "2.862f583179e347038c75895ceac08750", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.9a8ce5a0c44d4affb569cf64ed82dbb2" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "38082009", + "display": "HGB", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "38082009", + "display": "Hemoglobin (substance)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4244232", + "display": "Hemoglobin", + "userSelected": false + } + ], + "text": "HGB" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "valueString": "10.2 G/DL", + "interpretation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/Acuity", + "code": "L", + "display": "L", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/v3/ObservationInterpretation", + "code": "L", + "display": "Low", + "userSelected": false + } + ] + } + ], + "referenceRange": [ + { + "low": { + "value": 12.5, + "unit": "G/DL" + }, + "high": { + "value": 16, + "unit": "G/DL" + }, + "text": "12.5-16.0 G/DL" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Observation/2.1d376f381e404f629a4b668eb4bf7d72", + "resource": { + "resourceType": "Observation", + "id": "2.1d376f381e404f629a4b668eb4bf7d72", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-reportReference", + "valueReference": { + "reference": "DiagnosticReport/4.822796b740bb446791ef4e77d0756f42" + } + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory" + } + ], + "text": "laboratory" + } + ], + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.96", + "code": "31811003", + "display": "CARBON DIOXIDE", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "31811003", + "display": "Carbon dioxide (substance)", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "4135927", + "display": "Carbon dioxide", + "userSelected": false + } + ], + "text": "CARBON DIOXIDE" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "effectiveDateTime": "2009-06-08T12:09:00-04:00", + "issued": "2009-06-08T12:09:00-04:00", + "valueString": "26 MMOL/L", + "interpretation": [ + { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.DemoNamespace/Acuity", + "code": "N", + "display": "N", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/v3/ObservationInterpretation", + "code": "N", + "display": "Normal", + "userSelected": false + } + ] + } + ], + "referenceRange": [ + { + "low": { + "value": 22, + "unit": "MMOL/L" + }, + "high": { + "value": 31, + "unit": "MMOL/L" + }, + "text": "22-31 MMOL/L" + } + ] + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Procedure/7.bc640751160d4dce960769e09f6a7de1", + "resource": { + "resourceType": "Procedure", + "id": "7.bc640751160d4dce960769e09f6a7de1", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "status": "completed", + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.12", + "code": "3227", + "display": "Bronchoscopic bronchial thermoplasty, ablation of airway smooth muscle", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/sid/icd-9-cm/procedure", + "code": "32.27", + "display": "Bronchoscopic bronchial thermoplasty, ablation of airway smooth muscle", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "40756810", + "display": "Bronchoscopic bronchial thermoplasty, ablation of airway smooth muscle", + "userSelected": false + } + ], + "text": "Bronchoscopic bronchial thermoplasty, ablation of airway smooth muscle" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "performedDateTime": "2009-08-10T23:00:00-04:00" + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Procedure/7.93805cca981542d7a67e487f0b94944a", + "resource": { + "resourceType": "Procedure", + "id": "7.93805cca981542d7a67e487f0b94944a", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "status": "completed", + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.12", + "code": "0396", + "display": "Percutaneous denervation of facet", + "userSelected": true + }, + { + "system": "http://hl7.org/fhir/sid/icd-9-cm/procedure", + "code": "03.96", + "display": "Percutaneous denervation of facet", + "userSelected": false + }, + { + "system": "https://athena.ohdsi.org/", + "code": "2000195", + "display": "Percutaneous denervation of facet", + "userSelected": false + }, + { + "system": "http://snomed.info/sct", + "code": "50055008", + "display": "Percutaneous denervation of facet (procedure)", + "userSelected": false + } + ], + "text": "Percutaneous denervation of facet" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "performedDateTime": "2009-06-07T20:00:00-04:00" + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Procedure/7.2ed0fe30094a46e7bf6b3ebe69ead24a", + "resource": { + "resourceType": "Procedure", + "id": "7.2ed0fe30094a46e7bf6b3ebe69ead24a", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "status": "completed", + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.103", + "code": "465", + "display": "Treatment for Upper Respiratory Infection", + "userSelected": true + } + ], + "text": "Treatment for Upper Respiratory Infection" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "performedDateTime": "2009-07-01T12:00:00-04:00" + } + }, + { + "fullUrl": "https://api.rosetta.careevolution.com/Procedure/7.14e26bb09d7649b2a6ce10f794ca8960", + "resource": { + "resourceType": "Procedure", + "id": "7.14e26bb09d7649b2a6ce10f794ca8960", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure|3.1.1" + ], + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "status": "completed", + "code": { + "coding": [ + { + "system": "http://rosetta.careevolution.com/codes/Proprietary.CareEvolution/ProcedureCode", + "code": "OtherThymusOperations", + "display": "Oth thorac op thymus NOS", + "userSelected": true + } + ], + "text": "Other and unspecified thoracoscopic operations on thymus" + }, + "subject": { + "reference": "Patient/35b77437-425d-419c-90b5-af4bc433ebe9" + }, + "performedDateTime": "2009-06-07T20:30:00-04:00" + } + }, + { + "resource": { + "resourceType": "OperationOutcome", + "issue": [ + { + "severity": "warning", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAProcessingMessage", + "code": "CdaParsingWarning" + } + ], + "text": "Labs: Missing description in the reference dictionary for #labgroup_3. Line Number - 662, XPath - ClinicalDocument/component/structuredBody/component/section/entry/organizer/code/originalText/reference" + } + }, + { + "severity": "warning", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAProcessingMessage", + "code": "CdaParsingWarning" + } + ], + "text": "Labs: Missing description in the reference dictionary for #labgroup_5. Line Number - 701, XPath - ClinicalDocument/component/structuredBody/component/section/entry/organizer/code/originalText/reference" + } + }, + { + "severity": "warning", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAProcessingMessage", + "code": "CdaParsingWarning" + } + ], + "text": "Labs: Missing description in the reference dictionary for #labgroup_10. Line Number - 821, XPath - ClinicalDocument/component/structuredBody/component/section/entry/organizer/code/originalText/reference" + } + }, + { + "severity": "warning", + "code": "processing", + "details": { + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAProcessingMessage", + "code": "CdaParsingWarning" + } + ], + "text": "Labs: Missing description in the reference dictionary for #reportheader_12. Line Number - 860, XPath - ClinicalDocument/component/structuredBody/component/section/entry/organizer/code/originalText/reference" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Encounters" + } + ], + "text": "Encounters:\nTotal: 2\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 3 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Reports" + } + ], + "text": "Reports:\nTotal: 3\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 1 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Problems" + } + ], + "text": "Problems:\nTotal: 1\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 1 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Allergies" + } + ], + "text": "Allergies:\nTotal: 1\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 7 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Labs" + } + ], + "text": "Labs:\nTotal: 7\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 5 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Medications" + } + ], + "text": "Medications:\nTotal: 5\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 4 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Procedures" + } + ], + "text": "Procedures:\nTotal: 4\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#Total", + "valueInteger": 1 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#InvalidEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#DuplicateEntries", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Section#IgnoredEntries", + "valueInteger": 1 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDASectionSummary", + "code": "Vitals" + } + ], + "text": "Vitals:\nTotal: 1\nInvalidEntries: 0\nDuplicateEntries: 0\nIgnoredEntries: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Document#UnstructuredDocument", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Document#MissingEncompassingEncounter", + "valueBoolean": true + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Document#MissingLegalAuthenticator", + "valueBoolean": true + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Document#MissingSignatureCode", + "valueBoolean": true + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Document" + } + ], + "text": "Document:\nUnstructuredDocument: False\nMissingEncompassingEncounter: True\nMissingLegalAuthenticator: True\nMissingSignatureCode: True" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingGender", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingRace", + "valueBoolean": true + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingEthnicity", + "valueBoolean": true + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingDateOfBirth", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingPatientStreetAddress", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingPatientAddressCity", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingPatientAddressState", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingPatientAddressPostalCode", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingPatientPhoneNumber", + "valueBoolean": false + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#MissingPatientEMailAddress", + "valueBoolean": true + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Patient#ByIdentifierType", + "valueQuantity": { + "value": 1, + "unit": "1.3.6.1.4.1.37608", + "code": "1.3.6.1.4.1.37608" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Patient" + } + ], + "text": "Patient:\nMissingGender: False\nMissingRace: True\nMissingEthnicity: True\nMissingDateOfBirth: False\nMissingPatientStreetAddress: False\nMissingPatientAddressCity: False\nMissingPatientAddressState: False\nMissingPatientAddressPostalCode: False\nMissingPatientPhoneNumber: False\nMissingPatientEMailAddress: True\nByIdentifierType 1.3.6.1.4.1.37608: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#Total", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#MissingDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#MissingStopDate", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#MissingStopDateByStatus", + "valueQuantity": { + "value": 1, + "unit": "MissingStatus", + "code": "MissingStatus" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#MissingStopDateByStatus", + "valueQuantity": { + "value": 1, + "unit": "active:active", + "code": "active:active" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#ByType", + "valueQuantity": { + "value": 1, + "unit": "EncounterReason:Encounter Reason", + "code": "EncounterReason:Encounter Reason" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#ByType", + "valueQuantity": { + "value": 1, + "unit": "55607006:Problem", + "code": "55607006:Problem" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#ByStatus", + "valueQuantity": { + "value": 1, + "unit": "MissingStatus", + "code": "MissingStatus" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#ByStatus", + "valueQuantity": { + "value": 1, + "unit": "active:active", + "code": "active:active" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Problems#BySourceCodingSystem", + "valueQuantity": { + "value": 2, + "unit": "2.16.840.1.113883.6.103", + "code": "2.16.840.1.113883.6.103" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Problems" + } + ], + "text": "Problems:\nTotal: 2\nMissingDate: 0\nMissingStopDate: 2\nMissingStopDateByStatus MissingStatus: 1\nMissingStopDateByStatus active:active: 1\nByType EncounterReason:Encounter Reason: 1\nByType 55607006:Problem: 1\nByStatus MissingStatus: 1\nByStatus active:active: 1\nBySourceCodingSystem 2.16.840.1.113883.6.103: 2" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Procedures#Total", + "valueInteger": 4 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Procedures#MissingDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Procedures#ByStatus", + "valueQuantity": { + "value": 4, + "unit": "completed:completed", + "code": "completed:completed" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Procedures#BySourceCodingSystem", + "valueQuantity": { + "value": 2, + "unit": "2.16.840.1.113883.6.12", + "code": "2.16.840.1.113883.6.12" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Procedures#BySourceCodingSystem", + "valueQuantity": { + "value": 1, + "unit": "2.16.840.1.113883.6.103", + "code": "2.16.840.1.113883.6.103" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Procedures#BySourceCodingSystem", + "valueQuantity": { + "value": 1, + "unit": "Proprietary.CareEvolution", + "code": "Proprietary.CareEvolution" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Procedures" + } + ], + "text": "Procedures:\nTotal: 4\nMissingDate: 0\nByStatus completed:completed: 4\nBySourceCodingSystem 2.16.840.1.113883.6.12: 2\nBySourceCodingSystem 2.16.840.1.113883.6.103: 1\nBySourceCodingSystem Proprietary.CareEvolution: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#Total", + "valueInteger": 5 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#MissingOrderDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#MissingStopDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#MissingOrderingCaregiver", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#MissingCaregiver", + "valueInteger": 5 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#ByStatus", + "valueQuantity": { + "value": 5, + "unit": "completed:completed", + "code": "completed:completed" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#BySourceCodingSystem", + "valueQuantity": { + "value": 4, + "unit": "2.16.840.1.113883.6.88", + "code": "2.16.840.1.113883.6.88" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Medications#BySourceCodingSystem", + "valueQuantity": { + "value": 1, + "unit": "Proprietary.CareEvolution", + "code": "Proprietary.CareEvolution" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Medications" + } + ], + "text": "Medications:\nTotal: 5\nMissingOrderDate: 0\nMissingStopDate: 0\nMissingOrderingCaregiver: 0\nMissingCaregiver: 5\nByStatus completed:completed: 5\nBySourceCodingSystem 2.16.840.1.113883.6.88: 4\nBySourceCodingSystem Proprietary.CareEvolution: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Allergies#Total", + "valueInteger": 1 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Allergies#MissingReportedDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Allergies#MissingOnsetDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Allergies#MissingReactions", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Allergies#BySourceCodingSystem", + "valueQuantity": { + "value": 1, + "unit": "2.16.840.1.113883.6.96", + "code": "2.16.840.1.113883.6.96" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Allergies" + } + ], + "text": "Allergies:\nTotal: 1\nMissingReportedDate: 0\nMissingOnsetDate: 0\nMissingReactions: 0\nBySourceCodingSystem 2.16.840.1.113883.6.96: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabObservations#Total", + "valueInteger": 7 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabObservations#MissingDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabObservations#MissingPerformer", + "valueInteger": 7 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabObservations#BySourceCodingSystem", + "valueQuantity": { + "value": 6, + "unit": "2.16.840.1.113883.6.96", + "code": "2.16.840.1.113883.6.96" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabObservations#BySourceCodingSystem", + "valueQuantity": { + "value": 1, + "unit": "Proprietary.DemoNamespace", + "code": "Proprietary.DemoNamespace" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "LabObservations" + } + ], + "text": "LabObservations:\nTotal: 7\nMissingDate: 0\nMissingPerformer: 7\nBySourceCodingSystem 2.16.840.1.113883.6.96: 6\nBySourceCodingSystem Proprietary.DemoNamespace: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabPanels#Total", + "valueInteger": 4 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabPanels#MissingDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabPanels#WithSingleLabObservation", + "valueInteger": 3 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabPanels#BySourceCodingSystem", + "valueQuantity": { + "value": 3, + "unit": "2.16.840.1.113883.6.96", + "code": "2.16.840.1.113883.6.96" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/LabPanels#BySourceCodingSystem", + "valueQuantity": { + "value": 1, + "unit": "Proprietary.DemoNamespace", + "code": "Proprietary.DemoNamespace" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "LabPanels" + } + ], + "text": "LabPanels:\nTotal: 4\nMissingDate: 0\nWithSingleLabObservation: 3\nBySourceCodingSystem 2.16.840.1.113883.6.96: 3\nBySourceCodingSystem Proprietary.DemoNamespace: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Encounters#Total", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Encounters#MissingAdmitDate", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Encounters#MissingPatientClass", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Encounters#MissingLocation", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Encounters#MissingCaregiversByPatientClass", + "valueQuantity": { + "value": 1, + "unit": "PCPVisit:PCP Visit", + "code": "PCPVisit:PCP Visit" + } + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/CDAParsingSummary/Encounters#CaregiverRelationshipsByType", + "valueQuantity": { + "value": 1, + "unit": "ScheduledCaregiver", + "code": "ScheduledCaregiver" + } + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/CDAParsingSummary", + "code": "Encounters" + } + ], + "text": "Encounters:\nTotal: 2\nMissingAdmitDate: 0\nMissingPatientClass: 0\nMissingLocation: 2\nMissingCaregiversByPatientClass PCPVisit:PCP Visit: 1\nCaregiverRelationshipsByType ScheduledCaregiver: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#Total", + "valueInteger": 5 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#WellCodedCount", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#EnhancedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#ParsedCount", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#GarbageCount", + "valueInteger": 1 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/RosettaCodingUplift", + "code": "Medications" + } + ], + "text": "Medications:\nTotal: 5\nWellCodedCount: 2\nEnhancedCount: 0\nParsedCount: 2\nGarbageCount: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#Total", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#WellCodedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#EnhancedCount", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#ParsedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#GarbageCount", + "valueInteger": 0 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/RosettaCodingUplift", + "code": "Problems" + } + ], + "text": "Problems:\nTotal: 2\nWellCodedCount: 0\nEnhancedCount: 2\nParsedCount: 0\nGarbageCount: 0" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#Total", + "valueInteger": 4 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#WellCodedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#EnhancedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#ParsedCount", + "valueInteger": 2 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#GarbageCount", + "valueInteger": 2 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/RosettaCodingUplift", + "code": "Services" + } + ], + "text": "Services:\nTotal: 4\nWellCodedCount: 0\nEnhancedCount: 0\nParsedCount: 2\nGarbageCount: 2" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#Total", + "valueInteger": 7 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#WellCodedCount", + "valueInteger": 6 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#EnhancedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#ParsedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#GarbageCount", + "valueInteger": 1 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/RosettaCodingUplift", + "code": "Labs" + } + ], + "text": "Labs:\nTotal: 7\nWellCodedCount: 6\nEnhancedCount: 0\nParsedCount: 0\nGarbageCount: 1" + } + }, + { + "severity": "information", + "code": "informational", + "details": { + "extension": [ + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#Total", + "valueInteger": 4 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#WellCodedCount", + "valueInteger": 3 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#EnhancedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#ParsedCount", + "valueInteger": 0 + }, + { + "url": "https://quality.rosetta.careevolution.com/v1/Extensions/RosettaUplift/Coding#GarbageCount", + "valueInteger": 1 + } + ], + "coding": [ + { + "system": "https://quality.rosetta.careevolution.com/v1/CodeSystems/RosettaCodingUplift", + "code": "Reports" + } + ], + "text": "Reports:\nTotal: 4\nWellCodedCount: 3\nEnhancedCount: 0\nParsedCount: 0\nGarbageCount: 1" + } + } + ] + }, + "search": { + "mode": "outcome" + } + } + ] +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/r4_bundle.json b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/r4_bundle.json new file mode 100644 index 0000000..337b840 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/r4_bundle.json @@ -0,0 +1,65 @@ +{ + "resourceType": "Bundle", + "type": "batch-response", + "entry": [ + { + "fullUrl": "https://api.careevolutionapi.com/Patient/35b77437-425d-419c-90b5-af4bc433ebe9", + "resource": { + "resourceType": "Patient", + "id": "35b77437-425d-419c-90b5-af4bc433ebe9", + "meta": { + "source": "http://rosetta.careevolution.com/identifiers/CareEvolution/MRN/1.3.6.1.4.1.37608_1.3.6.1.4.1.37608" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "urn:oid:1.3.6.1.4.1.37608", + "value": "IheTestPatient" + }, + { + "system": "http://rosetta.careevolution.com/identifiers/Proprietary/1.3.6.1.4.1.37608", + "value": "IheTestPatient" + } + ], + "name": [ + { + "use": "official", + "family": "Smith", + "given": [ + "Patient" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "534-555-6666", + "use": "home" + } + ], + "gender": "female", + "birthDate": "1956-08-13", + "deceasedBoolean": false, + "address": [ + { + "use": "home", + "line": [ + "34 Drury Lane" + ], + "city": "Disney Land", + "state": "CA", + "postalCode": "90210" + } + ] + } + } + ] +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/risk_profile_bundle.json b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/risk_profile_bundle.json new file mode 100644 index 0000000..ea71644 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/risk_profile_bundle.json @@ -0,0 +1,350 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "entry": [ + { + "resource": { + "resourceType": "Patient", + "id": "9cee689b-6501-4349-af32-e6849e179a2f", + "meta": { + "lastUpdated": "2023-02-22T11:27:29.9499804+00:00", + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient", + "http://hl7.org/fhir/us/carin-bb/StructureDefinition/C4BB-Patient" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-religion", + "valueCodeableConcept": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.1", + "code": "Example", + "display": "Example", + "userSelected": true + } + ] + } + }, + { + "extension": [ + { + "url": "text", + "valueString": "N" + } + ], + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity" + }, + { + "extension": [ + { + "url": "text", + "valueString": "Black" + }, + { + "url": "detailed", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2056-0", + "display": "BLACK", + "userSelected": false + } + } + ], + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race" + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://test.careevolution.com/identifiers/CareEvolution/MRN/1.2.3.4.5.1.7_1.2.3.4.5.1.7", + "value": "0i56756845575l8yw6u886k4" + }, + { + "system": "http://test.careevolution.com/identifiers/1.2.3.4.5.1.7/1.2.3.4.5.1.7", + "value": "0i56756845575l8yw6u886k4" + }, + { + "system": "http://test.careevolution.com/identifiers/1.2.3.4.5.1.7/1.3.6.1.4.1.5641", + "value": "46274464" + } + ], + "name": [ + { + "use": "official", + "_use": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://careevolution.com/fhircodes#NameType", + "code": "LegalName", + "display": "Legal Name", + "userSelected": true + } + ] + } + } + ] + }, + "family": "Tester", + "given": [ + "Brittany" + ] + }, + { + "_use": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", + "valueCode": "unsupported" + }, + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://careevolution.com/fhircodes#NameType", + "code": "P", + "display": "Pseudonym", + "userSelected": true + } + ] + } + } + ] + }, + "family": "Tester", + "given": [ + "Brittany" + ] + } + ], + "telecom": [ + { + "system": "phone", + "_system": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://careevolution.com/fhircodes#ContactInfoType", + "code": "HomePhone", + "display": "Home Phone", + "userSelected": true + } + ] + } + } + ] + }, + "value": "tel:(680)555-1234", + "use": "home", + "_use": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.7", + "code": "HP", + "display": "primary home", + "userSelected": true + } + ] + } + } + ] + } + }, + { + "system": "phone", + "_system": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://careevolution.com/fhircodes#ContactInfoType", + "code": "OfficePhone", + "display": "Office Phone", + "userSelected": true + } + ] + } + } + ] + }, + "value": "tel:(548)555-8765", + "use": "work", + "_use": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.7", + "code": "WP", + "display": "work place", + "userSelected": true + } + ] + } + } + ] + } + }, + { + "_system": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", + "valueCode": "unsupported" + }, + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.7", + "code": "MC", + "display": "mobile contact", + "userSelected": true + } + ] + } + } + ] + }, + "value": "tel:(574)555-3737" + }, + { + "_system": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", + "valueCode": "unsupported" + }, + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.7", + "code": "Other", + "display": "Other", + "userSelected": true + } + ] + } + } + ] + }, + "value": "tel:(189)555-333" + } + ], + "gender": "male", + "_gender": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.5.1", + "code": "M", + "display": "Male", + "userSelected": true + }, + { + "system": "http://careevolution.com", + "code": "M", + "display": "Male", + "userSelected": false + }, + { + "system": "http://test.careevolution.com/codes/FhirCodesAlternate1/Gender", + "code": "M", + "display": "M", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/v3/AdministrativeGender", + "code": "M", + "display": "Male", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/administrative-gender", + "code": "male", + "display": "male", + "userSelected": false + }, + { + "system": "http://test.careevolution.com/codes/FhirCodes/Gender", + "code": "male", + "userSelected": false + } + ] + } + } + ] + }, + "birthDate": "1948-12-17", + "deceasedBoolean": false, + "address": [ + { + "use": "home", + "line": [ + "2608 Main Street" + ], + "city": "Anytown", + "state": "MI", + "postalCode": "48761" + } + ], + "maritalStatus": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.1", + "code": "M", + "display": "M", + "userSelected": true + } + ] + }, + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:oid:1.2.3.4.5.1.7", + "code": "ENGLISH", + "display": "ENGLISH", + "userSelected": true + } + ] + }, + "preferred": true + } + ] + } + } + ] +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/stu3_bundle.json b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/stu3_bundle.json new file mode 100644 index 0000000..9ea9611 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/stu3_bundle.json @@ -0,0 +1,2611 @@ +{ + "resourceType": "Bundle", + "type": "searchset", + "entry": [ + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "resource": { + "resourceType": "Patient", + "id": "3ead5e15-4da5-480b-8a94-ffea1e936809", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T15:08:05.723+00:00", + "security": [ + { + "system": "http://careevolution.com/accesspolicyname", + "code": "Standard Record Policy" + } + ] + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#patientInstitutions", + "valueCoding": { + "id": "patient-institution1", + "code": "BeaconInstitution", + "display": "BeaconInstitution" + } + } + ], + "identifier": [ + { + "id": "id1", + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest", + "value": "1234A" + }, + { + "id": "id2", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN", + "value": "1234A" + }, + { + "id": "id3", + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "name": [ + { + "id": "name1", + "use": "official", + "family": "Smith", + "given": [ + "John" + ] + } + ], + "gender": "male", + "_gender": { + "id": "gender1", + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "id": "gender2", + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/Gender", + "code": "M", + "display": "Male", + "userSelected": true + } + ] + } + } + ] + }, + "birthDate": "1961-01-02", + "_birthDate": { + "id": "birthdate1" + }, + "deceasedBoolean": false, + "_deceasedBoolean": { + "id": "deceased1" + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/0-3a4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "0-3a4295efae7fed11b9cc0e32e07a5c1b", + "contained": [ + { + "resourceType": "Patient", + "id": "patient", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "security": [ + { + "system": "http://careevolution.com/accesspolicyname", + "code": "Standard Record Policy" + } + ] + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#patientInstitutions", + "valueCoding": { + "id": "patient-institution1", + "code": "BeaconInstitution", + "display": "BeaconInstitution" + } + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest", + "value": "1234A" + }, + { + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN", + "value": "1234A" + }, + { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "name": [ + { + "use": "official", + "_use": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/NameType", + "code": "LegalName", + "display": "Legal Name", + "userSelected": true + } + ] + } + } + ] + }, + "family": "Smith", + "given": [ + "John" + ] + } + ], + "gender": "male", + "_gender": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/Gender", + "code": "M", + "display": "Male", + "userSelected": true + } + ] + } + } + ] + }, + "birthDate": "1961-01-02", + "deceasedBoolean": false + } + ], + "target": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#id1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#id2" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#id3" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#name1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#birthdate1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#deceased1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#gender1" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#gender2" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/targetElement", + "valueUri": "#patient-institution1" + } + ], + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809" + } + ], + "period": { + "start": "2022-12-19T15:08:05.723+00:00", + "end": "2022-12-19T15:08:05.717+00:00" + }, + "recorded": "2022-12-19T15:08:05.717+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "UPDATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ], + "entity": [ + { + "role": "source", + "whatReference": { + "reference": "#patient" + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Condition/5.d30a4c15f97fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Condition", + "id": "5.d30a4c15f97fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T23:58:55.387+00:00" + }, + "clinicalStatus": "resolved", + "_clinicalStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisStatus", + "code": "resolved", + "userSelected": true + } + ] + } + } + ] + }, + "_verificationStatus": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", + "valueCode": "unsupported" + }, + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisVerificationStatus", + "code": "unconfirmed", + "userSelected": true + } + ] + } + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/condition-category", + "code": "encounter-diagnosis" + }, + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisType", + "code": "EncounterDiagnosis", + "userSelected": true + }, + { + "system": "http://fhir.carevolution.com/codes/fhir-diagnosis-role/Reference", + "code": "CC", + "display": "Chief complaint", + "userSelected": false + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisCode", + "code": "flu", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "6142004", + "display": "Influenza (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "J11.1", + "display": "Influenza due to unidentified influenza virus with other respiratory manifestations", + "userSelected": false + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "onsetDateTime": "2022-12-19T02:31:55.382-05:00" + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Condition/5.3d4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Condition", + "id": "5.3d4295efae7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T15:08:05.75+00:00" + }, + "clinicalStatus": "inactive", + "_clinicalStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisStatus", + "code": "inactive", + "userSelected": true + } + ] + } + } + ] + }, + "verificationStatus": "confirmed", + "_verificationStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisVerificationStatus", + "code": "confirmed", + "userSelected": true + } + ] + } + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/condition-category", + "code": "encounter-diagnosis" + }, + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisType", + "code": "EncounterDiagnosis", + "userSelected": true + }, + { + "system": "http://fhir.carevolution.com/codes/fhir-diagnosis-role/Reference", + "code": "CC", + "display": "Chief complaint", + "userSelected": false + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisCode", + "code": "multiple sclerosis", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "24700007", + "display": "Multiple sclerosis (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "G35", + "display": "Multiple sclerosis", + "userSelected": false + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "onsetDateTime": "2022-12-18T18:55:05.675-05:00" + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Condition/5.3c4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Condition", + "id": "5.3c4295efae7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T15:08:05.74+00:00" + }, + "clinicalStatus": "active", + "_clinicalStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisStatus", + "code": "Active", + "userSelected": true + } + ] + } + } + ] + }, + "verificationStatus": "confirmed", + "_verificationStatus": { + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#term", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisVerificationStatus", + "code": "confirmed", + "userSelected": true + } + ] + } + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisType", + "code": "SECONDARY", + "display": "SECONDARY", + "userSelected": true + }, + { + "system": "http://fhir.carevolution.com/codes/CareEvolution/DiagnosisType", + "code": "Secondary", + "display": "Secondary", + "userSelected": false + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/DiagnosisCode", + "code": "crohns disease", + "userSelected": true + }, + { + "system": "http://snomed.info/sct", + "code": "34000006", + "display": "Crohn's disease (disorder)", + "userSelected": false + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "K50.90", + "display": "Crohn's disease, unspecified, without complications", + "userSelected": false + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "onsetDateTime": "2022-12-18T17:20:05.675-05:00" + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/4-d30a4c15f97fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "4-d30a4c15f97fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Condition/7.d30a4c15f97fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T23:58:55.387+00:00", + "end": "2022-12-19T23:58:55.387+00:00" + }, + "recorded": "2022-12-19T23:58:55.387+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/4-3d4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "4-3d4295efae7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Condition/7.3d4295efae7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T15:08:05.75+00:00", + "end": "2022-12-19T15:08:05.75+00:00" + }, + "recorded": "2022-12-19T15:08:05.75+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/4-3c4295efae7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "4-3c4295efae7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Condition/7.3c4295efae7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T15:08:05.74+00:00", + "end": "2022-12-19T15:08:05.74+00:00" + }, + "recorded": "2022-12-19T15:08:05.74+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.682d4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.682d4f56bc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:44:05.61+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-18T13:45:05.605-05:00", + "issued": "2022-12-18T13:45:05.605-05:00", + "valueQuantity": { + "value": 1690728474 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.d12b4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.d12b4f56bc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:44:03.04+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-18T14:56:03.025-05:00", + "issued": "2022-12-18T14:56:03.025-05:00", + "valueQuantity": { + "value": 1271126762 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.028d6f3ebc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.028d6f3ebc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:43:26.953+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-18T17:09:26.943-05:00", + "issued": "2022-12-18T17:09:26.943-05:00", + "valueQuantity": { + "value": 295302631 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.54e37a32bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.54e37a32bc7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:43:10.48+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-19T10:31:10.464-05:00", + "issued": "2022-12-19T10:31:10.464-05:00", + "valueQuantity": { + "value": 652309659 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.d24b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.d24b67f6bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:41:23.93+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-18T11:44:23.919-05:00", + "issued": "2022-12-18T11:44:23.919-05:00", + "valueQuantity": { + "value": 789662810 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.a94b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.a94b67f6bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:41:22.403+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-18T11:56:22.403-05:00", + "issued": "2022-12-18T11:56:22.403-05:00", + "valueQuantity": { + "value": 1582118206 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.f1b562d2bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.f1b562d2bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:40:27.56+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-18T22:38:27.543-05:00", + "issued": "2022-12-18T22:38:27.543-05:00", + "valueQuantity": { + "value": 252600252 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.2d6720babb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.2d6720babb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-20T03:22:56.517+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-19T11:21:45.582-05:00", + "issued": "2022-12-19T11:21:45.582-05:00", + "valueQuantity": { + "value": 1724700070 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.b185f6a7bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.b185f6a7bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:39:16.903+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-19T10:56:16.9-05:00", + "issued": "2022-12-19T10:56:16.9-05:00", + "valueQuantity": { + "value": 2126651898 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:37:09.9+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-19T07:24:09.891-05:00", + "issued": "2022-12-19T07:24:09.891-05:00", + "valueQuantity": { + "value": 941137949 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.1a5cf010bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.1a5cf010bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:35:02.83+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-19T10:44:02.819-05:00", + "issued": "2022-12-19T10:44:02.819-05:00", + "valueQuantity": { + "value": 1868889329 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Observation/1.f59dca04bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Observation", + "id": "1.f59dca04bb7fed11b9cc0e32e07a5c1b", + "meta": { + "extension": [ + { + "url": "http://hl7.org/fhir/4.0/StructureDefinition/extension-meta.source", + "valueUri": "http://fhir.carevolution.com/identifiers/CareEvolution/MRN/problemSelectorTest" + } + ], + "lastUpdated": "2022-12-19T16:34:40.32+00:00" + }, + "extension": [ + { + "url": "http://careevolution.com/fhirextensions#observation-contextID", + "valueString": "f39dca04-bb7f-ed11-b9cc-0e32e07a5c1b" + } + ], + "status": "unknown", + "code": { + "coding": [ + { + "system": "http://fhir.carevolution.com/codes/DemoNamespace/ObservationType", + "code": "BeaconFakeObservation", + "userSelected": true + } + ] + }, + "subject": { + "reference": "Patient/3ead5e15-4da5-480b-8a94-ffea1e936809", + "identifier": { + "system": "http://careevolution.com/fhir/PatientId", + "value": "3a4295ef-ae7f-ed11-b9cc-0e32e07a5c1b" + } + }, + "effectiveDateTime": "2022-12-19T11:11:40.299-05:00", + "issued": "2022-12-19T11:11:40.299-05:00", + "valueQuantity": { + "value": 1577878080 + } + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-682d4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-682d4f56bc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.682d4f56bc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:44:05.61+00:00", + "end": "2022-12-19T16:44:05.61+00:00" + }, + "recorded": "2022-12-19T16:44:05.61+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-d12b4f56bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-d12b4f56bc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.d12b4f56bc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:44:03.04+00:00", + "end": "2022-12-19T16:44:03.04+00:00" + }, + "recorded": "2022-12-19T16:44:03.04+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-028d6f3ebc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-028d6f3ebc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.028d6f3ebc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:43:26.953+00:00", + "end": "2022-12-19T16:43:26.953+00:00" + }, + "recorded": "2022-12-19T16:43:26.953+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-54e37a32bc7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-54e37a32bc7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.54e37a32bc7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:43:10.48+00:00", + "end": "2022-12-19T16:43:10.48+00:00" + }, + "recorded": "2022-12-19T16:43:10.48+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-d24b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-d24b67f6bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.d24b67f6bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:41:23.93+00:00", + "end": "2022-12-19T16:41:23.93+00:00" + }, + "recorded": "2022-12-19T16:41:23.93+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-a94b67f6bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-a94b67f6bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.a94b67f6bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:41:22.403+00:00", + "end": "2022-12-19T16:41:22.403+00:00" + }, + "recorded": "2022-12-19T16:41:22.403+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-f1b562d2bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-f1b562d2bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.f1b562d2bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:40:27.56+00:00", + "end": "2022-12-19T16:40:27.56+00:00" + }, + "recorded": "2022-12-19T16:40:27.56+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-2d6720babb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-2d6720babb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.2d6720babb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:39:45.6+00:00", + "end": "2022-12-20T03:22:56.517+00:00" + }, + "recorded": "2022-12-20T03:22:56.517+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "UPDATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-b185f6a7bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-b185f6a7bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.b185f6a7bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:39:16.903+00:00", + "end": "2022-12-19T16:39:16.903+00:00" + }, + "recorded": "2022-12-19T16:39:16.903+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-74ec7d5fbb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.74ec7d5fbb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:37:09.9+00:00", + "end": "2022-12-19T16:37:09.9+00:00" + }, + "recorded": "2022-12-19T16:37:09.9+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-1a5cf010bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-1a5cf010bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.1a5cf010bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:35:02.83+00:00", + "end": "2022-12-19T16:35:02.83+00:00" + }, + "recorded": "2022-12-19T16:35:02.83+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + }, + { + "fullUrl": "https://fhir.careevolution.com/Master.Adapter1.WebClient/api/fhir-stu3/Provenance/16-f59dca04bb7fed11b9cc0e32e07a5c1b", + "resource": { + "resourceType": "Provenance", + "id": "16-f59dca04bb7fed11b9cc0e32e07a5c1b", + "target": [ + { + "reference": "Observation/1.f59dca04bb7fed11b9cc0e32e07a5c1b" + } + ], + "period": { + "start": "2022-12-19T16:34:40.32+00:00", + "end": "2022-12-19T16:34:40.32+00:00" + }, + "recorded": "2022-12-19T16:34:40.32+00:00", + "activity": { + "system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", + "code": "CREATE" + }, + "agent": [ + { + "role": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ParticipationType", + "code": "AUT", + "display": "Author (originator)" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "author" + } + ], + "text": "Originating organization" + } + ], + "whoReference": { + "reference": "Organization/65db5f61-777c-ed11-b9cc-0e32e07a5c1b", + "display": "problemSelectorTest" + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://dicom.nema.org/resources/ontology/DCM", + "code": "110150", + "display": "Application" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "performer" + } + ], + "text": "Application" + } + ], + "whoReference": { + "display": "Sweetriver", + "identifier": { + "use": "official", + "system": "http://fhir.carevolution.com/typeid/Application", + "value": "1" + } + } + }, + { + "role": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/extra-security-role-type", + "code": "humanuser" + }, + { + "system": "http://hl7.org/fhir/provenance-participant-role", + "code": "enterer" + } + ], + "text": "User" + } + ], + "whoReference": { + "display": "SystemUser", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:3539c314-6e5b-4864-87c1-1195e7e2adcd" + } + } + } + ] + } + } + ] +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/x12.txt b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/x12.txt new file mode 100644 index 0000000..4b72ab9 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveData/x12.txt @@ -0,0 +1,44 @@ +ISA*00* *00* *ZZ*SUBMITTERID *ZZ*RECEIVERID *230616*1145*^*00501*000000001*0*T*:~ +GS*HC*SENDERCODE*RECEIVERCODE*20230627*11301505*123456789*X*005010X222A1~ +ST*837*0034*005010X223A1~ +BHT*0019*00*3920394930203*20100816*1615*CH~ +NM1*41*2*HOWDEE HOSPITAL*****46*0123456789~ +PER*IC*BETTY RUBBLE*TE*9195551111~ +NM1*40*2*BCBSNC*****46*987654321~ +HL*1**20*1~ +NM1*85*2*HOWDEE HOSPITAL*****XX*1245011012~ +N3*123 HOWDEE BLVD~ +N4*DURHAM*NC*27701~ +REF*EI*123456789~ +PER*IC*WILMA RUBBLE*TE*9195551111*FX*6145551212~ +HL*2*1*22*0~ +SBR*P*18*XYZ1234567******BL~ +NM1*IL*1*DOUGH*MARY****MI*24672148306~ +N3*BOX 12312~ +N4*DURHAM*NC*27715~ +DMG*D8*19670807*F~ +NM1*PR*2*BCBSNC*****PI*987654321~ +CLM*2235057*200***13:A:1***A**Y*Y~ +DTP*434*RD8*20100730-20100730~ +CL1*1*9*01~ +REF*F8*ASD0000123~ +HI*BK:25000~ +HI*BF:78901~ +HI*BR:4491:D8:20100730~ +HI*BH:41:D8:20100501*BH:27:D8:20100715*BH:33:D8:20100415*BH:C2:D8:20100410~ +HI*BE:30:::20~ +HI*BG:01~ +NM1*71*1*SMITH*ELIZABETH*AL***34*243898989~ +REF*1G*P97777~ +LX*1~ +SV2*0300*HC:81000*120*UN*1~ +DTP*472*D8*20100730~ +LX*2~ +SV2*0320*HC:76092*50*UN*1~ +DTP*472*D8*20100730~ +LX*3~ +SV2*0270*HC:J1120*30*UN*1~ +DTP*472*D8*20100730~ +SE*38*0034~ +GE*1*30~ +IEA*1*000000031~ diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveIdentityApiTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveIdentityApiTests.cs new file mode 100644 index 0000000..f9efa19 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveIdentityApiTests.cs @@ -0,0 +1,278 @@ +using CareEvolution.Orchestrate.Tests.Helpers; + +namespace CareEvolution.Orchestrate.Tests; + +public sealed class LiveIdentityApiTests : IDisposable +{ + private readonly HttpClient _httpClient = new(); + private readonly IdentityApi _api; + private const string DefaultSource = "source"; + + private static readonly Demographic Demographic = new() + { + FirstName = "John", + LastName = "Doe", + Dob = "1980-01-01", + Gender = "male", + }; + + private static readonly BlindedDemographic BlindedDemographic = new() + { + Data = LiveTestData.BlindedDemographicData, + Version = 1, + }; + + public LiveIdentityApiTests() + { + _api = LiveClients.CreateIdentityApi(_httpClient); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task AddOrUpdateRecordShouldAddRecord() + { + var response = await _api.AddOrUpdateRecordAsync( + new AddOrUpdateRecordRequest + { + Source = DefaultSource, + Identifier = Guid.NewGuid().ToString(), + Demographic = Demographic, + } + ); + + Assert.NotNull(response.MatchedPerson?.Id); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task AddOrUpdateRecordWithUrlUnsafeIdentifierShouldAddRecord() + { + var response = await _api.AddOrUpdateRecordAsync( + new AddOrUpdateRecordRequest + { + Source = DefaultSource, + Identifier = $"{Guid.NewGuid()}/", + Demographic = Demographic, + } + ); + + Assert.NotNull(response.MatchedPerson?.Id); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task AddOrUpdateBlindedRecordShouldAddRecord() + { + var response = await _api.AddOrUpdateBlindedRecordAsync( + new AddOrUpdateBlindedRecordRequest + { + Source = DefaultSource, + Identifier = Guid.NewGuid().ToString(), + BlindedDemographic = BlindedDemographic, + } + ); + + Assert.NotNull(response.MatchedPerson?.Id); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task AddOrUpdateBlindedRecordWithUrlUnsafeIdentifierShouldAddRecord() + { + var response = await _api.AddOrUpdateBlindedRecordAsync( + new AddOrUpdateBlindedRecordRequest + { + Source = DefaultSource, + Identifier = $"{Guid.NewGuid()}+/=", + BlindedDemographic = BlindedDemographic, + } + ); + + Assert.NotNull(response.MatchedPerson?.Id); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task GetPersonByRecordShouldMatch() + { + var (person, identifier) = await CreateRandomRecordAsync(); + var response = await _api.GetPersonByRecordAsync( + new CareEvolution.Orchestrate.Identity.Record + { + Source = DefaultSource, + Identifier = identifier, + } + ); + Assert.Equal(person.Id, response.Id); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task GetPersonByIdShouldMatch() + { + var (person, _) = await CreateRandomRecordAsync(); + var response = await _api.GetPersonByIdAsync(new GetPersonByIdRequest { Id = person.Id }); + Assert.Equal(person.Id, response.Id); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task MatchDemographicsShouldMatch() + { + var response = await _api.MatchDemographicsAsync(Demographic); + Assert.NotNull(response); + Assert.NotNull(response.MatchingPersons); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task MatchBlindedDemographicsShouldMatch() + { + var response = await _api.MatchBlindedDemographicsAsync(BlindedDemographic); + Assert.NotNull(response); + Assert.NotNull(response.MatchingPersons); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task DeleteRecordShouldDelete() + { + var (person, identifier) = await CreateRandomRecordAsync(); + var response = await _api.DeleteRecordAsync( + new CareEvolution.Orchestrate.Identity.Record + { + Source = DefaultSource, + Identifier = identifier, + } + ); + + Assert.Contains(response.ChangedPersons, changedPerson => changedPerson.Id == person.Id); + Assert.Contains( + response.ChangedPersons.SelectMany(changedPerson => changedPerson.Records), + record => record.Source == DefaultSource && record.Identifier == identifier + ); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task AddMatchGuidanceShouldReportChangedPersons() + { + var (firstPerson, firstIdentifier) = await CreateRandomRecordAsync(); + var (secondPerson, secondIdentifier) = await CreateRandomRecordAsync(); + + var response = await _api.AddMatchGuidanceAsync( + new AddMatchGuidanceRequest + { + RecordOne = new CareEvolution.Orchestrate.Identity.Record + { + Source = DefaultSource, + Identifier = firstIdentifier, + }, + RecordTwo = new CareEvolution.Orchestrate.Identity.Record + { + Source = DefaultSource, + Identifier = secondIdentifier, + }, + Action = "Match", + Comment = "Testing", + } + ); + + Assert.Contains( + response.ChangedPersons, + person => person.Id == firstPerson.Id || person.Id == secondPerson.Id + ); + } + + [LiveFact(LiveTestEnvironment.IdentityApiKey, LiveTestEnvironment.IdentityUrl)] + public async Task RemoveMatchGuidanceShouldSeparatePersons() + { + var (firstPerson, firstIdentifier) = await CreateRandomRecordAsync(); + var (secondPerson, secondIdentifier) = await CreateRandomRecordAsync(); + var recordOne = new CareEvolution.Orchestrate.Identity.Record + { + Source = DefaultSource, + Identifier = firstIdentifier, + }; + var recordTwo = new CareEvolution.Orchestrate.Identity.Record + { + Source = DefaultSource, + Identifier = secondIdentifier, + }; + + await _api.AddMatchGuidanceAsync( + new AddMatchGuidanceRequest + { + RecordOne = recordOne, + RecordTwo = recordTwo, + Action = "Match", + Comment = "Adding for removal testing", + } + ); + + var response = await _api.RemoveMatchGuidanceAsync( + new MatchGuidanceRequest + { + RecordOne = recordOne, + RecordTwo = recordTwo, + Comment = "Removal testing", + } + ); + + Assert.Contains( + response.ChangedPersons, + person => person.Id == firstPerson.Id || person.Id == secondPerson.Id + ); + } + + [LiveFact( + LiveTestEnvironment.IdentityApiKey, + LiveTestEnvironment.IdentityUrl, + LiveTestEnvironment.IdentityMetricsKey + )] + public async Task MonitoringIdentifierMetricsShouldHaveMetrics() + { + await CreateRandomRecordAsync(); + var response = await _api.Monitoring.IdentifierMetricsAsync(); + + Assert.False(string.IsNullOrWhiteSpace(response.Refreshed)); + Assert.True(response.TotalRecordCount > 0); + Assert.True(response.TotalPersonCount > 0); + Assert.NotNull(response.GlobalMetricsRecords); + Assert.Equal(string.Empty, response.GlobalMetricsRecords[0].Source); + Assert.Contains(response.SummaryMetricsRecords, record => record.Source == DefaultSource); + Assert.True(response.SourceTotals[0].TotalRecordCount > 0); + } + + [LiveFact( + LiveTestEnvironment.IdentityApiKey, + LiveTestEnvironment.IdentityUrl, + LiveTestEnvironment.IdentityMetricsKey + )] + public async Task MonitoringOverlapMetricsShouldHaveMetrics() + { + await CreateRandomRecordAsync(); + await CreateRandomRecordAsync(); + + var response = await _api.Monitoring.OverlapMetricsAsync(); + Assert.NotNull(response.DatasourceOverlapRecords); + Assert.Contains( + response.DatasourceOverlapRecords, + record => record.DatasourceA == DefaultSource + ); + Assert.Contains( + response.DatasourceOverlapRecords, + record => record.DatasourceB == DefaultSource + ); + Assert.True(response.DatasourceOverlapRecords[0].OverlapCount > 0); + } + + private async Task<(Person Person, string Identifier)> CreateRandomRecordAsync() + { + var identifier = Guid.NewGuid().ToString(); + var response = await _api.AddOrUpdateRecordAsync( + new AddOrUpdateRecordRequest + { + Source = DefaultSource, + Identifier = identifier, + Demographic = Demographic, + } + ); + return (response.MatchedPerson!, identifier); + } + + public void Dispose() + { + _httpClient.Dispose(); + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveLocalHashingApiTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveLocalHashingApiTests.cs new file mode 100644 index 0000000..c9999f2 --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/LiveLocalHashingApiTests.cs @@ -0,0 +1,52 @@ +using CareEvolution.Orchestrate.Tests.Helpers; + +namespace CareEvolution.Orchestrate.Tests; + +public sealed class LiveLocalHashingApiTests : IDisposable +{ + private readonly HttpClient _httpClient = new(); + private readonly LocalHashingApi _api; + + private static readonly Demographic Demographic = new() + { + FirstName = "John", + LastName = "Doe", + Dob = "1980-01-01", + Gender = "male", + }; + + public LiveLocalHashingApiTests() + { + _api = LiveClients.CreateLocalHashingApi(_httpClient); + } + + [LiveFact(LiveTestEnvironment.LocalHashingUrl)] + public async Task HashShouldHashByDemographic() + { + var response = await _api.HashAsync(Demographic); + + Assert.True(response.Version > 0); + Assert.Equal([], response.Advisories?.InvalidDemographicFields ?? []); + } + + [LiveFact(LiveTestEnvironment.LocalHashingUrl)] + public async Task HashWithInvalidDemographicFieldsShouldReturnAdvisories() + { + var response = await _api.HashAsync( + new Demographic + { + FirstName = Demographic.FirstName, + LastName = Demographic.LastName, + Dob = "121980-01-01", + } + ); + + Assert.True(response.Version > 0); + Assert.Equal(["dob"], response.Advisories?.InvalidDemographicFields ?? []); + } + + public void Dispose() + { + _httpClient.Dispose(); + } +} diff --git a/dotnet/tests/CareEvolution.Orchestrate.Tests/RouteBuilderTests.cs b/dotnet/tests/CareEvolution.Orchestrate.Tests/RouteBuilderTests.cs new file mode 100644 index 0000000..318efcb --- /dev/null +++ b/dotnet/tests/CareEvolution.Orchestrate.Tests/RouteBuilderTests.cs @@ -0,0 +1,65 @@ +namespace CareEvolution.Orchestrate.Tests; + +public sealed class RouteBuilderTests +{ + [Fact] + public void BuildShouldReturnPathWhenQueryIsEmpty() + { + var route = RouteBuilder.Build( + "/terminology/v1/fhir/r4/valueset", + [new KeyValuePair("name", null)] + ); + + Assert.Equal("/terminology/v1/fhir/r4/valueset", route); + } + + [Fact] + public void BuildShouldCombineMultipleQuerySets() + { + var route = RouteBuilder.Build( + "/convert/v1/hl7tofhirr4", + [new KeyValuePair("patientId", "1234")], + [ + new KeyValuePair("tz", "America/New_York"), + new KeyValuePair("processingHint", "lab"), + ] + ); + + Assert.Equal( + "/convert/v1/hl7tofhirr4?patientId=1234&tz=America%2FNew_York&processingHint=lab", + route + ); + } + + [Fact] + public void BuildQueryShouldSkipNullOrWhitespaceValues() + { + var query = RouteBuilder.BuildQuery([ + new KeyValuePair("name", "SNOMED"), + new KeyValuePair("blank", ""), + new KeyValuePair("spaces", " "), + new KeyValuePair("_count", "2"), + ]); + + Assert.Equal("name=SNOMED&_count=2", query); + } + + [Fact] + public void BuildQueryShouldEscapeKeysAndValues() + { + var query = RouteBuilder.BuildQuery([ + new KeyValuePair("concept:contains", "heart failure/test"), + new KeyValuePair("name", "SNOMED CT"), + ]); + + Assert.Equal("concept%3Acontains=heart%20failure%2Ftest&name=SNOMED%20CT", query); + } + + [Fact] + public void EscapeShouldEscapeReservedCharacters() + { + var escaped = RouteBuilder.Escape("SNOMED/CT Demo"); + + Assert.Equal("SNOMED%2FCT%20Demo", escaped); + } +}