diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 2e2cfa684d..0000000000
--- a/.dockerignore
+++ /dev/null
@@ -1,39 +0,0 @@
-**/.classpath
-**/.dockerignore
-**/.env
-**/.git
-**/.gitignore
-**/.project
-**/.settings
-**/.toolstarget
-**/.vs
-**/.vscode
-**/*.*proj.user
-**/*.dbmdl
-**/*.jfm
-**/azds.yaml
-**/bin
-**/charts
-**/docker-compose*
-**/Dockerfile*
-**/node_modules
-**/npm-debug.log
-**/obj
-**/secrets.dev.yaml
-**/values.dev.yaml
-**/build
-**/docs
-**/samples
-test
-tools
-!/tools/uploader-function
-*.md
-LICENSE
-
-# SourceLink
-!.git/HEAD
-!.git/config
-!.git/refs/heads
-
-# Local settings files
-local.settings.json
diff --git a/.editorconfig b/.editorconfig
index b967bb2abc..13485eba02 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -20,6 +20,55 @@ generated_code = true
[*.cs]
charset = utf-8
file_header_template = -------------------------------------------------------------------------------------------------\nCopyright (c) Microsoft Corporation. All rights reserved.\nLicensed under the MIT License (MIT). See LICENSE in the repo root for license information.\n-------------------------------------------------------------------------------------------------
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_braces = when_multiline:error
+csharp_style_namespace_declarations = file_scoped:error
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_prefer_system_threading_lock = true:suggestion
+csharp_style_expression_bodied_methods = true:silent
+csharp_style_expression_bodied_constructors = true:silent
+csharp_style_expression_bodied_operators = true:silent
+csharp_style_expression_bodied_properties = true:suggestion
+csharp_style_expression_bodied_indexers = true:suggestion
+csharp_style_expression_bodied_accessors = true:suggestion
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_throw_expression = true:suggestion
+csharp_style_prefer_null_check_over_type_check = true:suggestion
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = false:suggestion
+csharp_style_prefer_tuple_swap = true:suggestion
+csharp_style_prefer_utf8_string_literals = true:suggestion
+csharp_indent_labels = no_change
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+csharp_prefer_static_local_function = true:suggestion
+csharp_prefer_static_anonymous_function = true:suggestion
+csharp_style_prefer_readonly_struct = true:suggestion
+csharp_style_prefer_readonly_struct_member = true:suggestion
+csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
+csharp_style_conditional_delegate_call = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_extended_property_pattern = true:suggestion
+csharp_style_var_for_built_in_types = false:suggestion
+csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_style_var_elsewhere = false:suggestion
# Xml project files
[*.{csproj,dcproj}]
@@ -54,3 +103,85 @@ end_of_line = lf
[*.{cmd,bat}]
charset = utf-8
end_of_line = crlf
+
+[*.{cs,vb}]
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = true:error
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = false:silent
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
+dotnet_style_namespace_match_folder = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+end_of_line = crlf
+dotnet_style_readonly_field = true:suggestion
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
+dotnet_style_allow_multiple_blank_lines_experimental = true:silent
+dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
+dotnet_code_quality_unused_parameters = all:suggestion
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_qualification_for_field = false:error
+dotnet_style_qualification_for_property = false:error
+dotnet_style_qualification_for_method = false:error
+dotnet_style_qualification_for_event = false:error
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 1a71e97cb6..7a5013dbd4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -49,8 +49,6 @@ jobs:
- name: dotnet build
run: |
dotnet build Microsoft.Health.Dicom.sln -c Release -p:ContinuousIntegrationBuild=true -warnaserror
- dotnet build converter/dicom-cast/Microsoft.Health.DicomCast.sln -c Release -p:ContinuousIntegrationBuild=true -warnaserror
- dotnet build tools/Microsoft.Health.Dicom.Tools.sln -c Release -p:ContinuousIntegrationBuild=true -warnaserror
if: ${{ matrix.language == 'csharp' }}
- name: Perform CodeQL Analysis
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 6e07cda7dd..0000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Contributing to Medical Imaging Server for DICOM
-
-This document describes guidelines for contributing to the Medical Imaging Server for DICOM repo.
-
-## Submitting Pull Requests
-
-- **DO** submit all changes via pull requests (PRs). They will be reviewed and potentially be merged by maintainers after a peer review that includes at least one of the team members.
-- **DO** give PRs short but descriptive names.
-- **DO** write a useful but brief description of what the PR is for.
-- **DO** refer to any relevant issues and use [keywords](https://help.github.com/articles/closing-issues-using-keywords/) that automatically close issues when the PR is merged.
-- **DO** ensure each commit successfully builds. The entire PR must pass all checks before it will be merged.
-- **DO** address PR feedback in additional commits instead of amending.
-- **DO** assume that [Squash and Merge](https://blog.github.com/2016-04-01-squash-your-commits/) will be used to merge the commits unless specifically requested otherwise.
-- **DO NOT** submit "work in progress" PRs. A PR should only be submitted when it is considered ready for review.
-- **DO NOT** leave PRs active for more than 4 weeks without a commit. Stale PRs will be closed until they are ready for active development again.
-- **DO NOT** mix independent and unrelated changes in one PR.
-
-## Coding Style
-
-The coding style is enforced through [.NET analyzers](https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) and an [.editorconfig](.editorconfig) file. Contributors should ensure these guidelines are followed when making submissions.
-
-- **DO** address the .NET analyzer errors.
-- **DO** follow the [.editorconfig](.editorconfig) settings.
-
-## Creating Issues
-
-- **DO** use a descriptive title that identifies the issue or the requested feature.
-- **DO** write a detailed description of the issue or the requested feature.
-- **DO** provide details for issues you create:
- - Describe the expected and actual behavior.
- - Provide any relevant exception message or OperationOutcome.
-- **DO** subscribe to notifications for created issues in case there are any follow-up questions.
diff --git a/CredScanSuppressions.json b/CredScanSuppressions.json
deleted file mode 100644
index 230b956228..0000000000
--- a/CredScanSuppressions.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "tool": "Credential Scanner",
- "suppressions": [
- {
- "placeholder": "d8147077-d907-4551-8f40-90c6e86f3f0e",
- "_justification": "This is an example value and does not represent a real credential."
- },
- {
- "placeholder": "globalAdminServicePrincipal",
- "_justification": "Service principal for local testing."
- },
- {
- "placeholder": "123!@#passforCI#$",
- "_justification": "Test admin password for testing SQL server using docker during CI"
- },
- {
- "placeholder": "L0ca1P@ssw0rd",
- "_justification": "Test admin password for testing SQL server using docker locally"
- },
- {
- "placeholder": "T3stP@ssw0rd",
- "_justification": "Test admin password for validating the ARM template"
- }
- ]
-}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index bd36dc020b..5e824f4578 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,118 +1,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/GeoPol.xml b/GeoPol.xml
index c4ec4057b6..a7de96780b 100644
--- a/GeoPol.xml
+++ b/GeoPol.xml
@@ -15,7 +15,6 @@
.gitignore
- src\Microsoft.Health.Dicom.Web\wwwroot
GeoPol.xml
diff --git a/GitVersion.yml b/GitVersion.yml
deleted file mode 100644
index 10ea6cf34f..0000000000
--- a/GitVersion.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-mode: Mainline
-# assembly-version: If this number changes, other assemblies have to update references to the assembly
-assembly-versioning-scheme: Major
-assembly-file-versioning-scheme: MajorMinorPatch
-ignore:
- sha: []
-branches:
- main:
- is-release-branch: true
- feature:
- regex: ^(dependabot|dev|feature(s)?|personal|user(s)?)[/-]
- hotfix:
- tag: useBranchName
diff --git a/Microsoft.Health.Dicom.sln b/Microsoft.Health.Dicom.sln
index ca7d82f173..430a8a10b5 100644
--- a/Microsoft.Health.Dicom.sln
+++ b/Microsoft.Health.Dicom.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31825.309
@@ -10,233 +9,33 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{404C6C33
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
.config\dotnet-tools.json = .config\dotnet-tools.json
- GitVersion.yml = GitVersion.yml
global.json = global.json
nuget.config = nuget.config
EndProjectSection
EndProject
-Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker\docker-compose.dcproj", "{336B1FB4-EEF8-4E11-BDD5-818983D4E1CD}"
- ProjectSection(ProjectDependencies) = postProject
- {BFB96311-9B1A-41C1-ABF1-4F6522660084} = {BFB96311-9B1A-41C1-ABF1-4F6522660084}
- {C71E1BDD-2B8E-47F3-8801-AE95F5F39941} = {C71E1BDD-2B8E-47F3-8801-AE95F5F39941}
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Api", "src\Microsoft.Health.Dicom.Api\Microsoft.Health.Dicom.Api.csproj", "{B0570D75-E376-44AC-870B-87ECB54F0AE3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Api.UnitTests", "src\Microsoft.Health.Dicom.Api.UnitTests\Microsoft.Health.Dicom.Api.UnitTests.csproj", "{D7B538E5-8B3B-487C-8F6A-475F80C50DFE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Blob", "src\Microsoft.Health.Dicom.Blob\Microsoft.Health.Dicom.Blob.csproj", "{1FFFABFB-B30A-4AB8-8193-67016B1C5276}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Client", "src\Microsoft.Health.Dicom.Client\Microsoft.Health.Dicom.Client.csproj", "{D100EA60-8DC8-4576-A177-56BC7193BF4A}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Core", "src\Microsoft.Health.Dicom.Core\Microsoft.Health.Dicom.Core.csproj", "{E15123F6-5A28-4D86-A28D-30FCB699B9EE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Core.UnitTests", "src\Microsoft.Health.Dicom.Core.UnitTests\Microsoft.Health.Dicom.Core.UnitTests.csproj", "{FA0484A7-AA0C-4CC6-A75F-1D6B23DD847D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions", "src\Microsoft.Health.Dicom.Functions\Microsoft.Health.Dicom.Functions.csproj", "{C71E1BDD-2B8E-47F3-8801-AE95F5F39941}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions.Abstractions", "src\Microsoft.Health.Dicom.Functions.Abstractions\Microsoft.Health.Dicom.Functions.Abstractions.csproj", "{CFAD96C4-1AA3-442D-BADE-A93E4E936742}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions.Abstractions.UnitTests", "src\Microsoft.Health.Dicom.Functions.Abstractions.UnitTests\Microsoft.Health.Dicom.Functions.Abstractions.UnitTests.csproj", "{2D442B4F-C40A-4102-A332-8048A0655FDC}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions.App", "src\Microsoft.Health.Dicom.Functions.App\Microsoft.Health.Dicom.Functions.App.csproj", "{BB88616C-6208-43D0-B9EF-79FC0652A151}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions.Client", "src\Microsoft.Health.Dicom.Functions.Client\Microsoft.Health.Dicom.Functions.Client.csproj", "{F896C916-144F-412E-B3DE-C9D0D9B8EDD1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions.Client.UnitTests", "src\Microsoft.Health.Dicom.Functions.Client.UnitTests\Microsoft.Health.Dicom.Functions.Client.UnitTests.csproj", "{3D5D7F69-C766-450A-AA3D-00A50E115E9C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Functions.UnitTests", "src\Microsoft.Health.Dicom.Functions.UnitTests\Microsoft.Health.Dicom.Functions.UnitTests.csproj", "{3D679950-A578-45AD-AF89-FAF89580375F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Tests.Common", "src\Microsoft.Health.Dicom.Tests.Common\Microsoft.Health.Dicom.Tests.Common.csproj", "{0F57D85C-8FA4-4DBE-BF44-1CA5109125A5}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Tests.Integration", "test\Microsoft.Health.Dicom.Tests.Integration\Microsoft.Health.Dicom.Tests.Integration.csproj", "{DFB41ECC-726C-4DBA-8AD3-17FB0A2546CA}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.SqlServer", "src\Microsoft.Health.Dicom.SqlServer\Microsoft.Health.Dicom.SqlServer.csproj", "{A88DAB6A-BE0E-41BD-AB48-D1156FB6443C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.SqlServer.UnitTests", "src\Microsoft.Health.Dicom.SqlServer.UnitTests\Microsoft.Health.Dicom.SqlServer.UnitTests.csproj", "{ECC018C1-BFA8-44BE-B560-ACB05CA57251}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Web", "src\Microsoft.Health.Dicom.Web\Microsoft.Health.Dicom.Web.csproj", "{BFB96311-9B1A-41C1-ABF1-4F6522660084}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Web.Tests.E2E", "test\Microsoft.Health.Dicom.Web.Tests.E2E\Microsoft.Health.Dicom.Web.Tests.E2E.csproj", "{1D0ECFDA-2AF2-4796-995D-A7C6E18C9CD1}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{176641B3-297C-4E04-A83D-8F80F80485E8}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8C9A0050-5D22-4398-9F93-DDCD80B3BA51}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Azure", "src\Microsoft.Health.Dicom.Azure\Microsoft.Health.Dicom.Azure.csproj", "{EDF1BF37-3E55-4B6B-B922-C5EEDB71F782}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Azure.UnitTests", "src\Microsoft.Health.Dicom.Azure.UnitTests\Microsoft.Health.Dicom.Azure.UnitTests.csproj", "{5571FE42-1EDB-4E25-992A-A37467487DB3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Blob.UnitTests", "src\Microsoft.Health.Dicom.Blob.UnitTests\Microsoft.Health.Dicom.Blob.UnitTests.csproj", "{E2BD0627-CC6C-40D4-B875-00654FD059CF}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.SchemaManager", "src\Microsoft.Health.Dicom.SchemaManager\Microsoft.Health.Dicom.SchemaManager.csproj", "{5A517D8F-9DBB-4123-99C6-01686BCA4AC7}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.SchemaManager.UnitTests", "src\Microsoft.Health.Dicom.SchemaManager.UnitTests\Microsoft.Health.Dicom.SchemaManager.UnitTests.csproj", "{C8BB8AF3-DCD1-49A4-A082-9152686AD222}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.SchemaManager.Console", "src\Microsoft.Health.Dicom.SchemaManager.Console\Microsoft.Health.Dicom.SchemaManager.Console.csproj", "{7AE5FC7B-CE2D-4B5A-B8BB-9B673469EA88}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "forks", "forks", "{AEDC6C96-15FF-4AFB-BD49-3549211AACCF}"
- ProjectSection(SolutionItems) = preProject
- forks\.globalconfig = forks\.globalconfig
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.FellowOakDicom", "forks\Microsoft.Health.FellowOakDicom\Microsoft.Health.FellowOakDicom.csproj", "{ADD2B971-3C5C-429A-B954-A55FA0FA987D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Health.Dicom.WebUtilities", "src\Microsoft.Health.Dicom.WebUtilities\Microsoft.Health.Dicom.WebUtilities.csproj", "{A454E17D-3A0B-4EC3-85FF-B3CD56B369C8}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {336B1FB4-EEF8-4E11-BDD5-818983D4E1CD}.Debug|x64.ActiveCfg = Debug|Any CPU
- {336B1FB4-EEF8-4E11-BDD5-818983D4E1CD}.Debug|x64.Build.0 = Debug|Any CPU
- {336B1FB4-EEF8-4E11-BDD5-818983D4E1CD}.Release|x64.ActiveCfg = Release|Any CPU
- {336B1FB4-EEF8-4E11-BDD5-818983D4E1CD}.Release|x64.Build.0 = Release|Any CPU
- {B0570D75-E376-44AC-870B-87ECB54F0AE3}.Debug|x64.ActiveCfg = Debug|x64
- {B0570D75-E376-44AC-870B-87ECB54F0AE3}.Debug|x64.Build.0 = Debug|x64
- {B0570D75-E376-44AC-870B-87ECB54F0AE3}.Release|x64.ActiveCfg = Release|x64
- {B0570D75-E376-44AC-870B-87ECB54F0AE3}.Release|x64.Build.0 = Release|x64
- {D7B538E5-8B3B-487C-8F6A-475F80C50DFE}.Debug|x64.ActiveCfg = Debug|x64
- {D7B538E5-8B3B-487C-8F6A-475F80C50DFE}.Debug|x64.Build.0 = Debug|x64
- {D7B538E5-8B3B-487C-8F6A-475F80C50DFE}.Release|x64.ActiveCfg = Release|x64
- {D7B538E5-8B3B-487C-8F6A-475F80C50DFE}.Release|x64.Build.0 = Release|x64
- {1FFFABFB-B30A-4AB8-8193-67016B1C5276}.Debug|x64.ActiveCfg = Debug|x64
- {1FFFABFB-B30A-4AB8-8193-67016B1C5276}.Debug|x64.Build.0 = Debug|x64
- {1FFFABFB-B30A-4AB8-8193-67016B1C5276}.Release|x64.ActiveCfg = Release|x64
- {1FFFABFB-B30A-4AB8-8193-67016B1C5276}.Release|x64.Build.0 = Release|x64
{D100EA60-8DC8-4576-A177-56BC7193BF4A}.Debug|x64.ActiveCfg = Debug|x64
{D100EA60-8DC8-4576-A177-56BC7193BF4A}.Debug|x64.Build.0 = Debug|x64
{D100EA60-8DC8-4576-A177-56BC7193BF4A}.Release|x64.ActiveCfg = Release|x64
{D100EA60-8DC8-4576-A177-56BC7193BF4A}.Release|x64.Build.0 = Release|x64
- {E15123F6-5A28-4D86-A28D-30FCB699B9EE}.Debug|x64.ActiveCfg = Debug|x64
- {E15123F6-5A28-4D86-A28D-30FCB699B9EE}.Debug|x64.Build.0 = Debug|x64
- {E15123F6-5A28-4D86-A28D-30FCB699B9EE}.Release|x64.ActiveCfg = Release|x64
- {E15123F6-5A28-4D86-A28D-30FCB699B9EE}.Release|x64.Build.0 = Release|x64
- {FA0484A7-AA0C-4CC6-A75F-1D6B23DD847D}.Debug|x64.ActiveCfg = Debug|x64
- {FA0484A7-AA0C-4CC6-A75F-1D6B23DD847D}.Debug|x64.Build.0 = Debug|x64
- {FA0484A7-AA0C-4CC6-A75F-1D6B23DD847D}.Release|x64.ActiveCfg = Release|x64
- {FA0484A7-AA0C-4CC6-A75F-1D6B23DD847D}.Release|x64.Build.0 = Release|x64
- {C71E1BDD-2B8E-47F3-8801-AE95F5F39941}.Debug|x64.ActiveCfg = Debug|x64
- {C71E1BDD-2B8E-47F3-8801-AE95F5F39941}.Debug|x64.Build.0 = Debug|x64
- {C71E1BDD-2B8E-47F3-8801-AE95F5F39941}.Release|x64.ActiveCfg = Release|x64
- {C71E1BDD-2B8E-47F3-8801-AE95F5F39941}.Release|x64.Build.0 = Release|x64
- {CFAD96C4-1AA3-442D-BADE-A93E4E936742}.Debug|x64.ActiveCfg = Debug|x64
- {CFAD96C4-1AA3-442D-BADE-A93E4E936742}.Debug|x64.Build.0 = Debug|x64
- {CFAD96C4-1AA3-442D-BADE-A93E4E936742}.Release|x64.ActiveCfg = Release|x64
- {CFAD96C4-1AA3-442D-BADE-A93E4E936742}.Release|x64.Build.0 = Release|x64
- {2D442B4F-C40A-4102-A332-8048A0655FDC}.Debug|x64.ActiveCfg = Debug|x64
- {2D442B4F-C40A-4102-A332-8048A0655FDC}.Debug|x64.Build.0 = Debug|x64
- {2D442B4F-C40A-4102-A332-8048A0655FDC}.Release|x64.ActiveCfg = Release|x64
- {2D442B4F-C40A-4102-A332-8048A0655FDC}.Release|x64.Build.0 = Release|x64
- {BB88616C-6208-43D0-B9EF-79FC0652A151}.Debug|x64.ActiveCfg = Debug|x64
- {BB88616C-6208-43D0-B9EF-79FC0652A151}.Debug|x64.Build.0 = Debug|x64
- {BB88616C-6208-43D0-B9EF-79FC0652A151}.Release|x64.ActiveCfg = Release|x64
- {BB88616C-6208-43D0-B9EF-79FC0652A151}.Release|x64.Build.0 = Release|x64
- {F896C916-144F-412E-B3DE-C9D0D9B8EDD1}.Debug|x64.ActiveCfg = Debug|x64
- {F896C916-144F-412E-B3DE-C9D0D9B8EDD1}.Debug|x64.Build.0 = Debug|x64
- {F896C916-144F-412E-B3DE-C9D0D9B8EDD1}.Release|x64.ActiveCfg = Release|x64
- {F896C916-144F-412E-B3DE-C9D0D9B8EDD1}.Release|x64.Build.0 = Release|x64
- {3D5D7F69-C766-450A-AA3D-00A50E115E9C}.Debug|x64.ActiveCfg = Debug|x64
- {3D5D7F69-C766-450A-AA3D-00A50E115E9C}.Debug|x64.Build.0 = Debug|x64
- {3D5D7F69-C766-450A-AA3D-00A50E115E9C}.Release|x64.ActiveCfg = Release|x64
- {3D5D7F69-C766-450A-AA3D-00A50E115E9C}.Release|x64.Build.0 = Release|x64
- {3D679950-A578-45AD-AF89-FAF89580375F}.Debug|x64.ActiveCfg = Debug|x64
- {3D679950-A578-45AD-AF89-FAF89580375F}.Debug|x64.Build.0 = Debug|x64
- {3D679950-A578-45AD-AF89-FAF89580375F}.Release|x64.ActiveCfg = Release|x64
- {3D679950-A578-45AD-AF89-FAF89580375F}.Release|x64.Build.0 = Release|x64
- {0F57D85C-8FA4-4DBE-BF44-1CA5109125A5}.Debug|x64.ActiveCfg = Debug|x64
- {0F57D85C-8FA4-4DBE-BF44-1CA5109125A5}.Debug|x64.Build.0 = Debug|x64
- {0F57D85C-8FA4-4DBE-BF44-1CA5109125A5}.Release|x64.ActiveCfg = Release|x64
- {0F57D85C-8FA4-4DBE-BF44-1CA5109125A5}.Release|x64.Build.0 = Release|x64
- {DFB41ECC-726C-4DBA-8AD3-17FB0A2546CA}.Debug|x64.ActiveCfg = Debug|x64
- {DFB41ECC-726C-4DBA-8AD3-17FB0A2546CA}.Debug|x64.Build.0 = Debug|x64
- {DFB41ECC-726C-4DBA-8AD3-17FB0A2546CA}.Release|x64.ActiveCfg = Release|x64
- {DFB41ECC-726C-4DBA-8AD3-17FB0A2546CA}.Release|x64.Build.0 = Release|x64
- {A88DAB6A-BE0E-41BD-AB48-D1156FB6443C}.Debug|x64.ActiveCfg = Debug|x64
- {A88DAB6A-BE0E-41BD-AB48-D1156FB6443C}.Debug|x64.Build.0 = Debug|x64
- {A88DAB6A-BE0E-41BD-AB48-D1156FB6443C}.Release|x64.ActiveCfg = Release|x64
- {A88DAB6A-BE0E-41BD-AB48-D1156FB6443C}.Release|x64.Build.0 = Release|x64
- {ECC018C1-BFA8-44BE-B560-ACB05CA57251}.Debug|x64.ActiveCfg = Debug|x64
- {ECC018C1-BFA8-44BE-B560-ACB05CA57251}.Debug|x64.Build.0 = Debug|x64
- {ECC018C1-BFA8-44BE-B560-ACB05CA57251}.Release|x64.ActiveCfg = Release|x64
- {ECC018C1-BFA8-44BE-B560-ACB05CA57251}.Release|x64.Build.0 = Release|x64
- {BFB96311-9B1A-41C1-ABF1-4F6522660084}.Debug|x64.ActiveCfg = Debug|x64
- {BFB96311-9B1A-41C1-ABF1-4F6522660084}.Debug|x64.Build.0 = Debug|x64
- {BFB96311-9B1A-41C1-ABF1-4F6522660084}.Release|x64.ActiveCfg = Release|x64
- {BFB96311-9B1A-41C1-ABF1-4F6522660084}.Release|x64.Build.0 = Release|x64
- {1D0ECFDA-2AF2-4796-995D-A7C6E18C9CD1}.Debug|x64.ActiveCfg = Debug|x64
- {1D0ECFDA-2AF2-4796-995D-A7C6E18C9CD1}.Debug|x64.Build.0 = Debug|x64
- {1D0ECFDA-2AF2-4796-995D-A7C6E18C9CD1}.Release|x64.ActiveCfg = Release|x64
- {1D0ECFDA-2AF2-4796-995D-A7C6E18C9CD1}.Release|x64.Build.0 = Release|x64
- {EDF1BF37-3E55-4B6B-B922-C5EEDB71F782}.Debug|x64.ActiveCfg = Debug|x64
- {EDF1BF37-3E55-4B6B-B922-C5EEDB71F782}.Debug|x64.Build.0 = Debug|x64
- {EDF1BF37-3E55-4B6B-B922-C5EEDB71F782}.Release|x64.ActiveCfg = Release|x64
- {EDF1BF37-3E55-4B6B-B922-C5EEDB71F782}.Release|x64.Build.0 = Release|x64
- {5571FE42-1EDB-4E25-992A-A37467487DB3}.Debug|x64.ActiveCfg = Debug|x64
- {5571FE42-1EDB-4E25-992A-A37467487DB3}.Debug|x64.Build.0 = Debug|x64
- {5571FE42-1EDB-4E25-992A-A37467487DB3}.Release|x64.ActiveCfg = Release|x64
- {5571FE42-1EDB-4E25-992A-A37467487DB3}.Release|x64.Build.0 = Release|x64
- {E2BD0627-CC6C-40D4-B875-00654FD059CF}.Debug|x64.ActiveCfg = Debug|x64
- {E2BD0627-CC6C-40D4-B875-00654FD059CF}.Debug|x64.Build.0 = Debug|x64
- {E2BD0627-CC6C-40D4-B875-00654FD059CF}.Release|x64.ActiveCfg = Release|x64
- {E2BD0627-CC6C-40D4-B875-00654FD059CF}.Release|x64.Build.0 = Release|x64
- {5A517D8F-9DBB-4123-99C6-01686BCA4AC7}.Debug|x64.ActiveCfg = Debug|x64
- {5A517D8F-9DBB-4123-99C6-01686BCA4AC7}.Debug|x64.Build.0 = Debug|x64
- {5A517D8F-9DBB-4123-99C6-01686BCA4AC7}.Release|x64.ActiveCfg = Release|x64
- {5A517D8F-9DBB-4123-99C6-01686BCA4AC7}.Release|x64.Build.0 = Release|x64
- {C8BB8AF3-DCD1-49A4-A082-9152686AD222}.Debug|x64.ActiveCfg = Debug|x64
- {C8BB8AF3-DCD1-49A4-A082-9152686AD222}.Debug|x64.Build.0 = Debug|x64
- {C8BB8AF3-DCD1-49A4-A082-9152686AD222}.Release|x64.ActiveCfg = Release|x64
- {C8BB8AF3-DCD1-49A4-A082-9152686AD222}.Release|x64.Build.0 = Release|x64
- {7AE5FC7B-CE2D-4B5A-B8BB-9B673469EA88}.Debug|x64.ActiveCfg = Debug|x64
- {7AE5FC7B-CE2D-4B5A-B8BB-9B673469EA88}.Debug|x64.Build.0 = Debug|x64
- {7AE5FC7B-CE2D-4B5A-B8BB-9B673469EA88}.Release|x64.ActiveCfg = Release|x64
- {7AE5FC7B-CE2D-4B5A-B8BB-9B673469EA88}.Release|x64.Build.0 = Release|x64
- {ADD2B971-3C5C-429A-B954-A55FA0FA987D}.Debug|x64.ActiveCfg = Debug|x64
- {ADD2B971-3C5C-429A-B954-A55FA0FA987D}.Debug|x64.Build.0 = Debug|x64
- {ADD2B971-3C5C-429A-B954-A55FA0FA987D}.Release|x64.ActiveCfg = Release|x64
- {ADD2B971-3C5C-429A-B954-A55FA0FA987D}.Release|x64.Build.0 = Release|x64
- {A454E17D-3A0B-4EC3-85FF-B3CD56B369C8}.Debug|x64.ActiveCfg = Debug|x64
- {A454E17D-3A0B-4EC3-85FF-B3CD56B369C8}.Debug|x64.Build.0 = Debug|x64
- {A454E17D-3A0B-4EC3-85FF-B3CD56B369C8}.Release|x64.ActiveCfg = Release|x64
- {A454E17D-3A0B-4EC3-85FF-B3CD56B369C8}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {B0570D75-E376-44AC-870B-87ECB54F0AE3} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {D7B538E5-8B3B-487C-8F6A-475F80C50DFE} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {1FFFABFB-B30A-4AB8-8193-67016B1C5276} = {176641B3-297C-4E04-A83D-8F80F80485E8}
{D100EA60-8DC8-4576-A177-56BC7193BF4A} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {E15123F6-5A28-4D86-A28D-30FCB699B9EE} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {FA0484A7-AA0C-4CC6-A75F-1D6B23DD847D} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {C71E1BDD-2B8E-47F3-8801-AE95F5F39941} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {CFAD96C4-1AA3-442D-BADE-A93E4E936742} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {2D442B4F-C40A-4102-A332-8048A0655FDC} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {BB88616C-6208-43D0-B9EF-79FC0652A151} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {F896C916-144F-412E-B3DE-C9D0D9B8EDD1} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {3D5D7F69-C766-450A-AA3D-00A50E115E9C} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {3D679950-A578-45AD-AF89-FAF89580375F} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {0F57D85C-8FA4-4DBE-BF44-1CA5109125A5} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {DFB41ECC-726C-4DBA-8AD3-17FB0A2546CA} = {8C9A0050-5D22-4398-9F93-DDCD80B3BA51}
- {A88DAB6A-BE0E-41BD-AB48-D1156FB6443C} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {ECC018C1-BFA8-44BE-B560-ACB05CA57251} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {BFB96311-9B1A-41C1-ABF1-4F6522660084} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {1D0ECFDA-2AF2-4796-995D-A7C6E18C9CD1} = {8C9A0050-5D22-4398-9F93-DDCD80B3BA51}
- {EDF1BF37-3E55-4B6B-B922-C5EEDB71F782} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {5571FE42-1EDB-4E25-992A-A37467487DB3} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {E2BD0627-CC6C-40D4-B875-00654FD059CF} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {5A517D8F-9DBB-4123-99C6-01686BCA4AC7} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {C8BB8AF3-DCD1-49A4-A082-9152686AD222} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {7AE5FC7B-CE2D-4B5A-B8BB-9B673469EA88} = {176641B3-297C-4E04-A83D-8F80F80485E8}
- {ADD2B971-3C5C-429A-B954-A55FA0FA987D} = {AEDC6C96-15FF-4AFB-BD49-3549211AACCF}
- {A454E17D-3A0B-4EC3-85FF-B3CD56B369C8} = {176641B3-297C-4E04-A83D-8F80F80485E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {E370FB31-CF95-47D1-B1E1-863A77973FF8}
RESX_SortFileContentOnSave = True
+ SolutionGuid = {E370FB31-CF95-47D1-B1E1-863A77973FF8}
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 7504ac34e6..e2411a9959 100644
--- a/README.md
+++ b/README.md
@@ -1,91 +1,32 @@
# Medical Imaging Server for DICOM
> [!IMPORTANT]
-> 📢 **After more than 3 years, our team is planning to archive the DICOM server project to allow us to focus on delivering customer value to our [managed service offering on Azure](https://learn.microsoft.com/en-us/azure/healthcare-apis/dicom/overview). We plan to archive the DICOM server project on April 30, 2024**
+> 📢 **After more than 3 years, our team is planning to archive the DICOM server project to allow us to focus on delivering customer value to our [managed service offering on Azure](https://learn.microsoft.com/en-us/azure/healthcare-apis/dicom/overview).**
>
> Learn more in our recent [discussion post](https://github.com/microsoft/dicom-server/discussions/3401)
-The Medical Imaging Server for DICOM is an open source DICOM server that is easily deployed on Azure. It allows standards-based communication with any DICOMweb™ enabled systems, and injects DICOM metadata into a FHIR server to create a holistic view of patient data. The Medical Imaging Server for DICOM integrates tightly with the [FHIR Server for Azure](https://github.com/microsoft/fhir-server) enabling healthcare professionals, ISVs, and medical device vendors to create new and innovative solutions. FHIR is becoming an important standard for clinical data and provides extensibility to support integration of other types of data directly, or through references. By using the Medical Imaging Server for DICOM, organizations can store references to imaging data in FHIR and enable queries across clinical and imaging datasets.
+## Project Cleanup: Client Code Retention
-![Architecture](docs/images/DICOM-arch.png)
+This repository has undergone a significant cleanup. All code except for the client has been removed. If you need access to the removed code, it has been preserved in an archived branch for reference.
-The Medical Imaging Server for DICOM is a .NET Core implementation of DICOMweb™. [DICOMweb™](https://www.dicomstandard.org/using/dicomweb) is the DICOM Standard for web-based medical imaging. Details of our conformance to the standard can be found in our [Conformance Statement](docs/resources/conformance-statement.md).
+### Key Details
-## Managed service
+**Current State**: Only the client code remains in the main branch.
-Azure Health Data Service [DICOM service](https://docs.microsoft.com/en-us/azure/healthcare-apis/dicom/deploy-dicom-services-in-azure) is a managed service and recommended for production deployment.
+**Archived Code**: All other components have been moved to the [`archived`](https://github.com/microsoft/dicom-server/tree/archived) branch.
-Only the Nuget libraries released from this repo are meant to be used in production.
-Review [maintainance guide]( ./docs/resources/dicom-server-maintaince-guide.md) if you want to manage your own deployment from this repo.
+### Accessing the Archived Code
+To access the archived code:
-## Deploy the Medical Imaging Server for DICOM
+1. Clone the repository if you haven't already:
-The Medical Imaging Server for DICOM is designed to run on Azure. However, for development and test environments it can be deployed locally as a set of Docker containers to speed up development.
+ `git clone https://github.com/microsoft/dicom-server.git`
-### Deploy to Azure
+2. Switch to the archive branch:
-If you already have an Azure subscription, deploy the Medical Imaging Server for DICOM directly to Azure:
-
+ `git checkout archived`
-To sync your Medical Imaging Server for DICOM metadata directly into a FHIR server, deploy **DICOM Cast** (alongside a FHIR OSS Server and Medical Imaging Server for DICOM) via:
-
-
-
-For a complete set of instructions for how to deploy the Medical Imaging Server for DICOM to Azure, refer to the Quickstart [Deploy to Azure ](docs/quickstarts/deploy-via-azure.md).
-
-### Deploy locally
-
-Follow the [Development Setup Instructions](docs/development/setup.md) to deploy a local copy of the Medical Imaging Server for DICOM. Be aware that this deployment leverages the [Azurite container](https://github.com/Azure/Azurite) which emulates the Azure Storage API, and should not be used in production.
-
-Note that the webapp library, ARM templates and Web.Zip package are for testing purposes only, they are not recommended for production scenarios. These will not be versioned. You can find the artifact feed generated by the Medical Imaging Server for DICOM at the [Azure Devops Public Feed](https://microsofthealthoss.visualstudio.com/FhirServer/_packaging?_a=feed&feed=Public), including the versioned packages.
-
-## Quickstarts
-
-- [Deploy Medical Imaging Server for DICOM via Azure](docs/quickstarts/deploy-via-azure.md)
-- [Deploy Medical Imaging Server for DICOM via Docker](docs/quickstarts/deploy-via-docker.md)
-- [Set up DICOM Cast](docs/quickstarts/deploy-dicom-cast.md)
-
-## Tutorials
-
-- [Use the Medical Imaging Server for DICOM APIs](docs/tutorials/use-the-medical-imaging-server-apis.md)
-- [Use DICOMweb™ Standard APIs with C#](docs/tutorials/use-dicom-web-standard-apis-with-c%23.md)
-- [Use DICOMweb™ Standard APIs with Python](docs/tutorials/use-dicom-web-standard-apis-with-python.md)
-- [Use DICOMweb™ Standard APIs with cURL](docs/tutorials/use-dicom-web-standard-apis-with-curl.md)
-
-## How-to guides
-
-- [Configure Medical Imaging Server for DICOM server settings](docs/how-to-guides/configure-dicom-server-settings.md)
-- [Enable Authentication and retrieve an OAuth token](docs/how-to-guides/enable-authentication-with-tokens.md)
-- [Enable Authorization](docs/how-to-guides/enable-authorization.md)
-- [Pull Changes from Medical Imaging Server for DICOM with Change Feed](docs/how-to-guides/pull-changes-from-change-feed.md)
-- [Sync DICOM metadata to FHIR](docs/how-to-guides/sync-dicom-metadata-to-fhir.md)
-- [Extended Query Tags](docs/how-to-guides/extended-query-tags.md)
-
-## Concepts
-
-- [DICOM](docs/concepts/dicom.md)
-- [Change Feed](docs/concepts/change-feed.md)
-- [DICOM Cast](docs/concepts/dicom-cast.md)
-
-## Resources
-
-- [FAQ](docs/resources/faq.md)
-- [Conformance Statement](docs/resources/conformance-statement.md)
-- [Health Check API](docs/resources/health-check-api.md)
-- [Performance Guidance](docs/resources/performance-guidance.md)
-- [Api Versions](docs/api-versioning.md)
-
-## Development
-
-- [Setup](docs/development/setup.md)
-- [Code Organization](docs/development/code-organization.md)
-- [Naming Guidelines](docs/development/naming-guidelines.md)
-- [Exception handling](docs/development/exception-handling.md)
-- [Tests](docs/development/tests.md)
-- [Identity Server Authentication](docs/development/identity-server-authentication.md)
-- [Roles](docs/development/roles.md)
-- [API Versioning (developer)](docs/development/api-versioning-developers.md)
## Contributing
@@ -98,14 +39,8 @@ a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow th
provided by the bot. You will only need to do this once across all repositories using our CLA.
There are many other ways to contribute to Medical Imaging Server for DICOM.
-* [Submit bugs](https://github.com/Microsoft/dicom-server/issues) and help us verify fixes as they are checked in.
-* Review the [source code changes](https://github.com/Microsoft/dicom-server/pulls).
-* Engage with Medical Imaging Server for DICOM users and developers on [StackOverflow](https://stackoverflow.com/questions/tagged/medical-imaging-server-for-dicom).
* Join the [#dicomonazure](https://twitter.com/hashtag/dicomonazure?f=tweets&vertical=default) discussion on Twitter.
-* [Contribute bug fixes](CONTRIBUTING.md).
-
-See [Contributing to Medical Imaging Server for DICOM](CONTRIBUTING.md) for more information.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
-contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
\ No newline at end of file
diff --git a/build/.vsts-ci.yml b/build/.vsts-ci.yml
deleted file mode 100644
index 18c79baf6b..0000000000
--- a/build/.vsts-ci.yml
+++ /dev/null
@@ -1,150 +0,0 @@
-# DESCRIPTION:
-# Builds, tests and packages the solution for the CI build configuration.
-name: $(SourceBranchName)-$(Date:yyyyMMdd)$(Rev:-r)
-
-variables:
-- template: ci/variables.yml
-
-parameters:
-- name: push
- displayName: Push
- type: boolean
- default: true
-
-trigger:
- branches:
- include:
- - main
- paths:
- include:
- - '*'
- exclude:
- - '*.md'
- - docs
-
-pr: none
-
-stages:
-- stage: UpdateVersion
- displayName: 'Determine Semantic Version'
- jobs:
- - job: GitVersion
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: ./common/update-semver.yml
-
-- stage: BuildElectron
- displayName: 'Build Electron Tool'
- jobs:
- - job: NodeJs
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: common/build-electron.yml
-
-- stage: BuildDotNet
- displayName: 'Build and Run Unit Tests'
- dependsOn:
- - UpdateVersion
- variables:
- assemblySemVer: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.AssemblySemVer']]
- assemblySemFileVer: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.AssemblySemFileVer']]
- informationalVersion: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.InformationalVersion']]
- majorMinorPatch: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.MajorMinorPatch']]
- nuGetVersion: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.SemVer']]
- jobs:
- - job: DotNet
- displayName: 'Build DICOM Projects'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: common/build-dotnet.yml
-
-- stage: AnalyzeSecurity
- displayName: 'Run Security Analysis'
- dependsOn:
- - BuildDotNet
- jobs:
- - job: Guardian
- pool:
- vmImage: 'windows-latest'
- steps:
- - template: common/analyze.yml
-
-- stage: DeployTestEnvironment
- displayName: 'Deploy Test Environment'
- dependsOn:
- - BuildDotNet
- jobs:
- - template: ci/deploy.yml
-
-- stage: ValidateAPIVersioning
- displayName: 'Detect Breaking Changes In API'
- dependsOn:
- - BuildDotNet
- jobs:
- - template: common/versioning.yml
-
-- stage: DeployFeaturesEnabledWebapp
- displayName: 'Deploy features-enabled webapp'
- dependsOn:
- - DeployTestEnvironment
- jobs:
- - template: ci/deploy-features-enabled-webapp.yml
-
-- stage: RunIntegrationTests
- displayName: 'Run Integration Tests'
- dependsOn:
- - DeployTestEnvironment
- jobs:
- - template: ci/run-integration-tests.yml
-
-- stage: RunE2ETests
- displayName: 'Run E2E Tests'
- dependsOn:
- - DeployTestEnvironment
- jobs:
- - template: ci/run-e2e-tests.yml
-
-- stage: RunE2EFeaturesEnabledTests
- displayName: 'Run E2E features-enabled tests'
- dependsOn:
- - DeployFeaturesEnabledWebapp
- jobs:
- - template: ci/run-e2e-features-enabled-tests.yml
-
-- stage: PublishNuget
- displayName: 'Publish NuGet Packages'
- condition: eq(${{ parameters.push }}, true)
- dependsOn:
- - AnalyzeSecurity
- - ValidateAPIVersioning
- - RunIntegrationTests
- - RunE2ETests
- - RunE2EFeaturesEnabledTests
- jobs:
- - job: PublishNugets
- pool:
- vmImage: 'windows-latest'
- steps:
- - template: ci/publish-nuget.yml
-
-- stage: PublishContainer
- displayName: 'Publish Docker CI Container'
- dependsOn:
- - AnalyzeSecurity
- - ValidateAPIVersioning
- - RunIntegrationTests
- - RunE2ETests
- - RunE2EFeaturesEnabledTests
- jobs:
- - job: 'Docker'
- displayName: 'Build and Push Docker Images'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: common/docker-build-push.yml
- parameters:
- tag: $(imageTag)
- push: ${{ parameters.push }}
diff --git a/build/.vsts-pr.arm.yml b/build/.vsts-pr.arm.yml
deleted file mode 100644
index 459c3c652e..0000000000
--- a/build/.vsts-pr.arm.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-# DESCRIPTION:
-# Validates the ARM templates in the event of changes
-
-pr:
- branches:
- include:
- - main
- paths:
- include:
- - build
- - samples/templates
-
-trigger: none
-
-variables:
-- template: pr/variables.yml
-
-# Note: ARMory currently only works on Windows
-pool:
- vmImage: 'windows-latest'
-
-steps:
- - task: AzureResourceManagerTemplateDeployment@3
- displayName: 'Validate ARM Template'
- inputs:
- deploymentScope: 'Resource Group'
- azureResourceManagerConnection: '$(azureSubscriptionName)'
- subscriptionId: '$(azureSubscriptionId)'
- action: 'Create Or Update Resource Group'
- resourceGroupName: '$(appServicePlanResourceGroup)'
- location: 'West US 2'
- templateLocation: 'Linked artifact'
- csmFile: '$(Build.Repository.LocalPath)/samples/templates/default-azuredeploy.json'
- overrideParameters: '-serviceName "$(deploymentName)" -location "$(resourceGroupRegion)" -sqlAdminPassword "T3stP@ssw0rd"'
- deploymentMode: 'Validation'
- deploymentName: 'ValidateDicom$(System.PullRequest.PullRequestNumber)'
-
- - template: common/analyze.yml
- parameters:
- analyzeBinaries: false
- analyzePackages: false
- runAntiMalware: false
- credScanDirectory: '$(Build.Repository.LocalPath)/samples/templates'
diff --git a/build/.vsts-pr.dotnet.yml b/build/.vsts-pr.dotnet.yml
deleted file mode 100644
index 83f5c2e136..0000000000
--- a/build/.vsts-pr.dotnet.yml
+++ /dev/null
@@ -1,112 +0,0 @@
-# DESCRIPTION:
-# Builds, tests and packages the .NET solutions
-
-pr:
- branches:
- include:
- - main
- paths:
- include:
- - '*'
- exclude:
- - '*.md'
- - docs
- - samples/templates
- - tools/dicom-web-electron
-
-trigger: none
-
-variables:
-- template: pr/variables.yml
-
-stages:
-- stage: UpdateVersion
- displayName: 'Determine Semantic Version'
- jobs:
- - job: GitVersion
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: ./common/update-semver.yml
- - powershell: |
- $buildNumber = "$(GitVersion.SemVer)" -replace "\.", ""
- Write-Host "##vso[build.updatebuildnumber]$buildNumber"
- Write-Host "Updated build number to '$buildNumber'"
- name: SetBuildVersion
-
-- stage: BuildDotNet
- displayName: 'Build and Run Unit Tests'
- dependsOn:
- - UpdateVersion
- variables:
- assemblySemVer: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.AssemblySemVer']]
- assemblySemFileVer: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.AssemblySemFileVer']]
- informationalVersion: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.InformationalVersion']]
- majorMinorPatch: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.MajorMinorPatch']]
- nuGetVersion: $[stageDependencies.UpdateVersion.GitVersion.outputs['DicomVersion.GitVersion.SemVer']]
- jobs:
- - job: DotNet
- displayName: 'Build DICOM Projects'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: common/build-dotnet.yml
- parameters:
- packageArtifacts: true
- packageNugets: false
-
-- stage: DockerBuild
- displayName: 'Build Docker'
- dependsOn:
- - BuildDotNet
- jobs:
- - job: Docker
- displayName: docker build
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - template: common/docker-build-push.yml
- parameters:
- tag: $(imageTag)
- push: false
-
-- stage: AnalyzeSecurity
- displayName: 'Run Security Analysis'
- dependsOn:
- - BuildDotNet
- jobs:
- - job: Guardian
- pool:
- vmImage: 'windows-latest'
- steps:
- - template: common/analyze.yml
- parameters:
- analyzePackages: false
-
-- stage: ValidateAPIVersioning
- displayName: 'Detect Breaking REST API Changes'
- dependsOn:
- - BuildDotNet
- jobs:
- - template: common/versioning.yml
-
-- stage: RunIntegrationTests
- displayName: 'Run Integration tests'
- dependsOn:
- - BuildDotNet
- jobs:
- - template: pr/run-integration-tests.yml
-
-- stage: RunE2ETests
- displayName: 'Run E2E tests'
- dependsOn:
- - BuildDotNet
- jobs:
- - template: pr/run-e2e-tests.yml
-
-- stage: RunE2EFeaturesEnabledTests
- displayName: 'Run E2E features-enabled tests'
- dependsOn:
- - BuildDotNet
- jobs:
- - template: pr/run-e2e-features-enabled-tests.yml
diff --git a/build/.vsts-pr.node.yml b/build/.vsts-pr.node.yml
deleted file mode 100644
index 88f171bb13..0000000000
--- a/build/.vsts-pr.node.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-# DESCRIPTION:
-# Builds the JavaScript tools
-
-pr:
- branches:
- include:
- - main
- paths:
- include:
- - build
- - tools/dicom-web-electron
-
-variables:
-- template: pr/variables.yml
-
-trigger: none
-
-pool:
- vmImage: 'ubuntu-latest'
-
-steps:
- - template: common/build-electron.yml
- # Task is needed to bypass Guardian issue until Guardian team has fixed their script to get sdk for linux platforms
- - task: UseDotNet@2
- displayName: 'Use .NET Core sdk'
- inputs:
- version: '3.1.201'
- - template: common/analyze.yml
- parameters:
- analyzeARMTemplates: false
- analyzeBinaries: false
- analyzePackages: false
- runAntiMalware: false
- credScanDirectory: '$(Build.Repository.LocalPath)/tools/dicom-web-electron'
diff --git a/build/ci/add-aad-test-environment.yml b/build/ci/add-aad-test-environment.yml
deleted file mode 100644
index 8567f1c626..0000000000
--- a/build/ci/add-aad-test-environment.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-steps:
-
-- task: AzureKeyVault@1
- displayName: 'Azure Key Vault: resolute-oss-tenant-info'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- KeyVaultName: 'resolute-oss-tenant-info'
-
-- task: AzurePowerShell@5
- displayName: Setup Aad Test Tenant
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: inlineScript
- Inline: |
- Install-Module -Name AzureAD -Repository PSGallery -Scope CurrentUser -AcceptLicense -Force
- Import-Module -Name AzureAD
-
- $tenantId = "$(tenant-id)"
-
- # Get admin token
- $username = "$(tenant-admin-user-name)"
- $password_raw =
- @"
- $(tenant-admin-user-password)
- "@
- $password = ConvertTo-SecureString -AsPlainText $password_raw -Force
- $adminCredential = New-Object PSCredential $username,$password
-
- $adTokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/token"
- $resource = "https://graph.windows.net/"
-
- $body = @{
- grant_type = "password"
- username = $username
- password = $password_raw
- resource = $resource
- client_id = "1950a258-227b-4e31-a9cf-717495945fc2" # Microsoft Azure PowerShell
- }
-
- # If a deleted keyvault exists, remove it
- if (Get-AzKeyVault -VaultName "$(deploymentName)-ts" -Location "$(resourceGroupRegion)" -InRemovedState)
- {
- Write-Host "A deleted keyvault '$(deploymentName)-ts' found in '$(resourceGroupRegion)'. Attempting to remove."
- Remove-AzKeyVault -VaultName "$(deploymentName)-ts" -InRemovedState -Location "$(resourceGroupRegion)" -Force
- }
- else
- {
- Write-Host "A deleted keyvault '$(deploymentName)-ts' not found in '$(resourceGroupRegion)' - this is normal"
- }
-
- $response = Invoke-RestMethod -Method 'Post' -Uri $adTokenUrl -ContentType "application/x-www-form-urlencoded" -Body $body
- Connect-AzureAD -TenantId $tenantId -AadAccessToken $response.access_token -AccountId $username
-
- Import-Module $(System.DefaultWorkingDirectory)/samples/scripts/PowerShell/DicomServer.psd1
- Import-Module $(System.DefaultWorkingDirectory)/release/scripts/PowerShell/DicomServerRelease/DicomServerRelease.psd1
-
- $output = Add-AadTestAuthEnvironment -TestAuthEnvironmentPath $(System.DefaultWorkingDirectory)/testauthenvironment.json -EnvironmentName $(deploymentName) -TenantAdminCredential $adminCredential -EnvironmentLocation $(resourceGroupRegion) -TenantIdDomain $tenantId
diff --git a/build/ci/deploy-features-enabled-webapp.yml b/build/ci/deploy-features-enabled-webapp.yml
deleted file mode 100644
index 5b06bb261e..0000000000
--- a/build/ci/deploy-features-enabled-webapp.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-jobs:
-- job: provision
- displayName: 'Provision DICOM'
- pool:
- vmImage: 'windows-latest'
- variables:
- - name: sqlAdminPassword
- value: $[ stageDependencies.DeployTestEnvironment.provision.outputs['deploy.sqlAdminPassword'] ]
- steps:
-
- - task: AzureKeyVault@1
- displayName: 'Azure Key Vault: resolute-oss-tenant-info'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- KeyVaultName: 'resolute-oss-tenant-info'
-
- - task: AzurePowerShell@5
- displayName: 'Features-enabled webapp infrastructure deployment'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: InlineScript
- Inline: |
- Add-Type -AssemblyName System.Web
-
- $deployPath = "$(System.DefaultWorkingDirectory)/release/templates"
-
- $additionalProperties = @{
- "DicomServer__Features__EnableDataPartitions" = "true"
- "DicomServer__Features__EnableLatestApiVersion" = "true"
- }
-
- $templateParameters = @{
- serviceName = "$(deploymentName)"
- appServicePlanResourceGroup = "$(appServicePlanResourceGroup)"
- appServicePlanName = "$(appServicePlanName)"
- additionalDicomServerConfigProperties = $additionalProperties
- sqlAdminPassword = "$(sqlAdminPassword)"
- securityAuthenticationAuthority = "https://login.microsoftonline.com/$(tenant-id)"
- securityAuthenticationAudience = "$(testApplicationResource)"
- }
-
- New-AzResourceGroupDeployment -Name "$(deploymentName)-features-enabled-webapp" -ResourceGroupName "$(resourceGroupName)" -TemplateFile $deployPath/featuresenabled-azuredeploy.json -TemplateParameterObject $templateParameters -Verbose
-
- - task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'deploy'
-
- - task: AzureRMWebAppDeployment@4
- displayName: 'Features-enabled webapp package deployment'
- inputs:
- connectionType: 'AzureRM'
- azureSubscription: $(azureSubscriptionName)
- webAppKind: 'webApp'
- webAppName: '$(deploymentName)-featuresenabled'
- package: '$(System.ArtifactsDirectory)/deploy/Microsoft.Health.Dicom.Web.zip'
- takeAppOfflineFlag: true
- deploymentType: zipDeploy
diff --git a/build/ci/deploy.yml b/build/ci/deploy.yml
deleted file mode 100644
index 90853bb6dd..0000000000
--- a/build/ci/deploy.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-jobs:
-- job: Provision
- displayName: 'Provision DICOM'
- pool:
- vmImage: 'windows-latest'
- steps:
- - task: AzurePowerShell@5
- displayName: 'New Resource Group'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: InlineScript
- Inline: |
- New-AzResourceGroup -Name "$(resourceGroupName)" -Location "$(resourceGroupRegion)" -Force
-
- - template: add-aad-test-environment.yml
-
- - task: AzurePowerShell@5
- name: deploy
- displayName: 'New Azure resource group deployment'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: InlineScript
- Inline: |
- Add-Type -AssemblyName System.Web
-
- $deployPath = "$(System.DefaultWorkingDirectory)/samples/templates"
-
- $additionalProperties = @{
- "SqlServer__DeleteAllDataOnStartup" = "$(deleteDataOnStartup)"
- "DicomServer__Security__Authorization__Enabled" = "true"
- }
-
- $sqlAdminPassword = "$(-join((((33,35,37,38,42,43,45,46,95) + (48..57) + (65..90) + (97..122) | Get-Random -Count 20) + ((33,35,37,38,42,43,45,46,95) | Get-Random -Count 1) + ((48..57) | Get-Random -Count 1) + ((65..90) | Get-Random -Count 1) + ((97..122) | Get-Random -Count 1) | Get-Random -Count 24) | % {[char]$_}))"
- Write-Host "##vso[task.setvariable variable=sqlAdminPassword;isSecret=true;isOutput=true]$sqlAdminPassword"
-
- $templateParameters = @{
- serviceName = "$(deploymentName)"
- functionAppName = "$(deploymentName)-functions"
- appServicePlanResourceGroup = "$(appServicePlanResourceGroup)"
- appServicePlanName = "$(appServicePlanName)"
- additionalDicomServerConfigProperties = $additionalProperties
- sqlAdminPassword = $sqlAdminPassword
- securityAuthenticationAuthority = "https://login.microsoftonline.com/$(tenant-id)"
- securityAuthenticationAudience = "$(testApplicationResource)"
- deployPackage = $false
- }
-
- $deployment = New-AzResourceGroupDeployment -Name "$(deploymentName)" -ResourceGroupName "$(resourceGroupName)" -TemplateFile $deployPath/default-azuredeploy.json -TemplateParameterObject $templateParameters -Verbose
-
- Set-AzKeyVaultAccessPolicy -VaultName "$(deploymentName)" -ObjectId $(azureServiceConnectionOid) -PermissionsToSecrets list,get -BypassObjectIdValidation
-
- $storageAccountName = $deployment.Outputs['storageAccountName'].Value
- Write-Host "##vso[task.setvariable variable=azureStorageAccountName;isOutput=true]$storageAccountName"
-
- - task: DownloadBuildArtifacts@0
- displayName: 'Download Deployment Binaries'
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'deploy'
-
- - task: AzureRMWebAppDeployment@4
- displayName: 'Deploy Dicom Web App'
- inputs:
- connectionType: 'AzureRM'
- azureSubscription: $(azureSubscriptionName)
- webAppKind: 'webApp'
- webAppName: '$(deploymentName)'
- package: '$(System.ArtifactsDirectory)/deploy/Microsoft.Health.Dicom.Web.zip'
- takeAppOfflineFlag: true
- deploymentType: zipDeploy
-
- - task: AzureRMWebAppDeployment@4
- displayName: 'Deploy Dicom Functions'
- inputs:
- connectionType: 'AzureRM'
- azureSubscription: $(azureSubscriptionName)
- webAppKind: 'functionApp'
- webAppName: '$(deploymentName)-functions'
- package: '$(System.ArtifactsDirectory)/deploy/Microsoft.Health.Dicom.Functions.App.zip'
- takeAppOfflineFlag: true
- deploymentType: zipDeploy
diff --git a/build/ci/publish-nuget.yml b/build/ci/publish-nuget.yml
deleted file mode 100644
index b3beda14df..0000000000
--- a/build/ci/publish-nuget.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-steps:
- - task: UseDotNet@2
- displayName: 'Use .NET Core sdk'
- inputs:
- useGlobalJson: true
-
- - task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'nuget'
-
- - task: NuGetAuthenticate@0
- displayName: 'NuGet Authenticate'
-
- - task: NuGetCommand@2
- displayName: 'NuGet push'
- inputs:
- command: push
- publishVstsFeed: 'InternalBuilds'
- allowPackageConflicts: true
-
- - task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(build.artifactStagingDirectory)'
- artifactName: 'symbols'
-
- - task: PublishSymbols@2
- displayName: 'Publish Symbols'
- inputs:
- symbolsFolder: '$(build.artifactStagingDirectory)/symbols'
- searchPattern: '**/*.pdb'
- symbolServerType: 'TeamServices'
- indexSources: false # done in build step
diff --git a/build/ci/run-e2e-features-enabled-tests.yml b/build/ci/run-e2e-features-enabled-tests.yml
deleted file mode 100644
index 6864fe0346..0000000000
--- a/build/ci/run-e2e-features-enabled-tests.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-jobs:
-- job: SetupAndRun
- displayName: 'Feature-Specific E2E Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - task: AzurePowerShell@5
- displayName: 'Set Secret Variables'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: inlineScript
- Inline: |
- $secrets = Get-AzKeyVaultSecret -VaultName $(deploymentName)-ts
-
- foreach($secret in $secrets)
- {
- $environmentVariableName = $secret.Name.Replace("--","_")
- $secretValue = Get-AzKeyVaultSecret -VaultName $(deploymentName)-ts -Name $secret.Name -AsPlainText
- Write-Host "##vso[task.setvariable variable=$environmentVariableName]$secretValue"
- }
-
- - bash: |
- echo "##vso[task.setvariable variable=testEnvironmentUrl]$(testServerFeaturesEnabledUrl)"
- echo "##vso[task.setvariable variable=Resource]$(testServerFeaturesEnabledUrl)"
- echo "##vso[task.setvariable variable=security_scope]$(testApplicationScope)"
- echo "##vso[task.setvariable variable=security_resource]$(testApplicationResource)"
- echo "##vso[task.setvariable variable=security_enabled]true"
-
- dotnet dev-certs https
- displayName: 'Setup Authentication'
-
- - template: ../common/run-e2e-features-enabled-tests.yml
diff --git a/build/ci/run-e2e-tests.yml b/build/ci/run-e2e-tests.yml
deleted file mode 100644
index 2eb4ede4db..0000000000
--- a/build/ci/run-e2e-tests.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-jobs:
-- job: SetupAndRun
- displayName: 'E2E Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - task: AzurePowerShell@5
- displayName: 'Set Secret Variables'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: inlineScript
- Inline: |
- $secrets = Get-AzKeyVaultSecret -VaultName $(deploymentName)-ts
-
- foreach($secret in $secrets)
- {
- $environmentVariableName = $secret.Name.Replace("--","_")
- $secretValue = Get-AzKeyVaultSecret -VaultName $(deploymentName)-ts -Name $secret.Name -AsPlainText
- Write-Host "##vso[task.setvariable variable=$environmentVariableName]$secretValue"
- }
-
- - task: AzurePowerShell@5
- displayName: 'Create Azure Storage SAS Token'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: inlineScript
- Inline: |
- $keys = Get-AzStorageAccountKey -Name '$(azureStorageAccountName)' -ResourceGroupName '$(deploymentName)'
- $primaryKey = $keys[0].Value
-
- $start = Get-Date
- $end = $start.AddHours(1)
- $cxt = New-AzStorageContext -StorageAccountName '$(azureStorageAccountName)' -StorageAccountKey $primaryKey
- $token = New-AzStorageAccountSASToken -Service Blob -ResourceType Container,Object -Permission 'rwdl' -Start $start -ExpiryTime $end -Context $cxt
- $connectionString = "BlobEndpoint=https://$(azureStorageAccountName).blob.core.windows.net;SharedAccessSignature=$token"
-
- Write-Host "##vso[task.setvariable variable=Tests__Export__ConnectionString]$connectionString"
-
- - bash: |
- echo "##vso[task.setvariable variable=testEnvironmentUrl]$(testServerUrl)"
- echo "##vso[task.setvariable variable=Resource]$(testServerUrl)"
- echo "##vso[task.setvariable variable=security_scope]$(testApplicationScope)"
- echo "##vso[task.setvariable variable=security_resource]$(testApplicationResource)"
- echo "##vso[task.setvariable variable=security_enabled]true"
-
- dotnet dev-certs https
- displayName: 'Setup Authentication'
-
- - template: ../common/run-e2e-tests.yml
- parameters:
- externalStorageTests: false
-
- variables:
- azureStorageAccountName: $[ stageDependencies.DeployTestEnvironment.Provision.outputs['deploy.azureStorageAccountName'] ]
diff --git a/build/ci/run-integration-tests.yml b/build/ci/run-integration-tests.yml
deleted file mode 100644
index c1eb434870..0000000000
--- a/build/ci/run-integration-tests.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-jobs:
-- job: SetupAndRun
- displayName: 'Integration Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - task: AzurePowerShell@5
- displayName: 'Prepare for Testing'
- inputs:
- azureSubscription: $(azureSubscriptionName)
- azurePowerShellVersion: latestVersion
- ScriptType: InlineScript
- Inline: |
- $sqlConnectionString = Get-AzKeyVaultSecret -VaultName $(deploymentName) -Name "SqlServerConnectionString" -AsPlainText
- $blobConnectionString = Get-AzKeyVaultSecret -VaultName $(deploymentName) -Name "AzureStorageConnectionString" -AsPlainText
-
- Write-Host "##vso[task.setvariable variable=BlobStore__ConnectionString]$blobConnectionString"
- Write-Host "##vso[task.setvariable variable=SqlServer__ConnectionString]$sqlConnectionString"
-
- dotnet dev-certs https
-
- - template: ../common/run-integration-tests.yml
diff --git a/build/ci/variables.yml b/build/ci/variables.yml
deleted file mode 100644
index 8e1ec2f8d8..0000000000
--- a/build/ci/variables.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-variables:
- deploymentName: 'dcm-ci-permanent'
- testServerUrl: 'https://$(deploymentName).azurewebsites.net/'
- testServerFeaturesEnabledUrl: 'https://$(deploymentName)-featuresenabled.azurewebsites.net/'
- testApplicationScope: 'https://$(deploymentName).resoluteopensource.onmicrosoft.com/.default'
- testApplicationResource: 'https://$(deploymentName).resoluteopensource.onmicrosoft.com'
- resourceGroupName: $(deploymentName)
- resourceGroupRegion: 'southcentralus'
- appServicePlanResourceGroup: 'msh-dicom-pr'
- appServicePlanName: $(appServicePlanResourceGroup)-$(resourceGroupRegion)
- azureServiceConnectionOid: '5e9db4f6-b680-4408-a85b-af0ad8ef185d'
- azureSubscriptionName: 'Dicom OSS'
- buildConfiguration: 'Release'
- imageTag: '$(build.BuildNumber)'
- azureContainerRegistry: 'dicomoss.azurecr.io'
- deleteDataOnStartup: 'false'
- skipNugetSecurityAnalysis: 'true' # NuGet config contains multiple feeds but meets exception criteria
diff --git a/build/common/analyze.yml b/build/common/analyze.yml
deleted file mode 100644
index 70bf675c6f..0000000000
--- a/build/common/analyze.yml
+++ /dev/null
@@ -1,97 +0,0 @@
-parameters:
- analyzeARMTemplates: true
- analyzeBinaries: true
- analyzePackages: true
- runAntiMalware: true
- credScanDirectory: '$(Build.SourcesDirectory)'
-
-steps:
-- ${{ if eq(parameters.analyzeBinaries, 'true') }}:
- - task: DownloadBuildArtifacts@0
- displayName: 'Download DICOM Binaries'
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(Agent.TempDirectory)/artifacts'
- artifactName: 'deploy'
-
-- ${{ if eq(parameters.analyzePackages, 'true') }}:
- - task: DownloadBuildArtifacts@0
- displayName: 'Download DICOM NuGet Packages'
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(Build.SourcesDirectory)/artifacts'
- artifactName: 'nuget'
-
-- ${{ if eq(parameters.analyzeBinaries, 'true') }}:
- - task: ExtractFiles@1
- displayName: 'Extract DICOM Web Server Binaries'
- inputs:
- archiveFilePatterns: '$(Agent.TempDirectory)/artifacts/deploy/Microsoft.Health.Dicom.Web.zip'
- destinationFolder: '$(Build.SourcesDirectory)/artifacts/web'
-
-- ${{ if eq(parameters.runAntiMalware, 'true') }}:
- - task: AntiMalware@4
- inputs:
- InputType: 'Basic'
- ScanType: 'CustomScan'
- FileDirPath: '$(Build.SourcesDirectory)'
- EnableServices: true
- TreatSignatureUpdateFailureAs: 'Standard'
- SignatureFreshness: 'OneDay'
- TreatStaleSignatureAs: 'Error'
-
-- ${{ if eq(parameters.analyzeARMTemplates, 'true') }}:
- - task: Armory@2
- inputs:
- targetDirectory: '$(Build.SourcesDirectory)\samples\templates'
- targetFiles: 'f|*.json'
- excludePassesFromLog: false
-
-- task: CredScan@3
- inputs:
- scanFolder: ${{ parameters.credScanDirectory }}
- outputFormat: 'sarif'
- suppressionsFile: 'CredScanSuppressions.json'
- verboseOutput: true
-
-- task: SdtReport@2
- inputs:
- GdnExportAllTools: false
- GdnExportGdnToolArmory: ${{ eq(parameters.analyzeARMTemplates, 'true') }}
- GdnExportGdnToolCredScan: true
-
-- task: PublishSecurityAnalysisLogs@3
- inputs:
- ArtifactName: 'CodeAnalysisLogs'
- ArtifactType: 'Container'
- AllTools: false
- AntiMalware: ${{ eq(parameters.runAntiMalware, 'true') }}
- APIScan: false
- Armory: ${{ eq(parameters.analyzeARMTemplates, 'true') }}
- Bandit: false
- BinSkim: false
- CodesignValidation: false
- CredScan: true
- CSRF: false
- ESLint: false
- Flawfinder: false
- FortifySCA: false
- FxCop: false
- ModernCop: false
- MSRD: false
- PoliCheck: false
- RoslynAnalyzers: false
- SDLNativeRules: false
- Semmle: false
- SpotBugs: false
- TSLint: false
- WebScout: false
- ToolLogsNotFoundAction: 'Standard'
-
-- task: PostAnalysis@2
- inputs:
- GdnBreakAllTools: false
- GdnBreakGdnToolArmory: ${{ eq(parameters.analyzeARMTemplates, 'true') }}
- GdnBreakGdnToolCredScan: true
diff --git a/build/common/build-dotnet.yml b/build/common/build-dotnet.yml
deleted file mode 100644
index 0eb720b076..0000000000
--- a/build/common/build-dotnet.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-parameters:
- packageArtifacts: true
- packageNugets: true
-
-steps:
- - task: UseDotNet@2
- displayName: 'Use .NET 6 sdk'
- inputs:
- version: 6.x
-
- - task: UseDotNet@2
- displayName: 'Use .NET sdk'
- inputs:
- useGlobalJson: true
-
- - task: DotNetCoreCLI@2
- displayName: 'dotnet build $(buildConfiguration)'
- inputs:
- command: 'build'
- projects: '**/*.csproj'
- arguments: '--configuration $(buildConfiguration) -warnaserror -p:AssemblyVersion="$(assemblySemVer)" -p:FileVersion="$(assemblySemFileVer)" -p:InformationalVersion="$(informationalVersion)" -p:ContinuousIntegrationBuild=true'
-
- - task: DotNetCoreCLI@2
- displayName: 'dotnet test UnitTests'
- inputs:
- command: test
- projects: '**/*UnitTests/*.csproj'
- arguments: '--configuration $(buildConfiguration) --no-build'
-
- - task: ComponentGovernanceComponentDetection@0
- inputs:
- scanType: 'Register'
- verbosity: 'Verbose'
- alertWarningLevel: 'High'
- failOnAlert: true
-
- - ${{ if eq(parameters.packageArtifacts, 'true') }}:
- - template: package.yml
-
- - ${{ if eq(parameters.packageNugets, 'true') }}:
- - template: package-nugets.yml
diff --git a/build/common/build-electron.yml b/build/common/build-electron.yml
deleted file mode 100644
index fe6789e7ce..0000000000
--- a/build/common/build-electron.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-steps:
- - task: NodeTool@0
- displayName: 'Install Node.js'
- inputs:
- versionSpec: '17.x'
-
- # TODO: Add validation
- - script: npm ci
- displayName: 'npm ci'
- workingDirectory: '$(Build.Repository.LocalPath)/tools/dicom-web-electron'
-
- - task: ComponentGovernanceComponentDetection@0
- displayName: 'Component Governance'
- inputs:
- scanType: 'Register'
- verbosity: 'Verbose'
- alertWarningLevel: 'High'
- failOnAlert: true
diff --git a/build/common/docker-build-push.yml b/build/common/docker-build-push.yml
deleted file mode 100644
index 735875aa89..0000000000
--- a/build/common/docker-build-push.yml
+++ /dev/null
@@ -1,131 +0,0 @@
-# DESCRIPTION:
-# Builds and pushes a docker image for dicom-server and dicom-cast
-
-parameters:
- - name: tag
- type: string
- - name: push
- type: boolean
- default: true
-
-steps:
- - ${{ if eq(parameters.push, true) }}:
- - task: Docker@2
- displayName: Login
- inputs:
- command: login
- containerRegistry: '$(azureContainerRegistry)'
-
- - task: Docker@2
- displayName: 'Build dicom-server'
- inputs:
- command: 'build'
- containerRegistry: '$(azureContainerRegistry)'
- Dockerfile: 'src/Microsoft.Health.Dicom.Web/Dockerfile'
- buildContext: '$(Build.Repository.LocalPath)'
- arguments: '--build-arg BUILD_CONFIGURATION=Release --build-arg CONTINUOUS_INTEGRATION_BUILD=true'
- repository: dicom-server
- tags: ${{ parameters.tag }}
-
- - task: Docker@2
- displayName: 'Build dicom-cast'
- inputs:
- command: 'build'
- containerRegistry: '$(azureContainerRegistry)'
- Dockerfile: 'converter/dicom-cast/src/Microsoft.Health.DicomCast.Hosting/Dockerfile'
- buildContext: '$(Build.Repository.LocalPath)'
- arguments: '--build-arg BUILD_CONFIGURATION=Release --build-arg CONTINUOUS_INTEGRATION_BUILD=true'
- repository: dicom-cast
- tags: |
- ${{ parameters.tag }}
- latest
-
- - task: Docker@2
- displayName: 'Build dicom-functions'
- inputs:
- command: 'build'
- containerRegistry: '$(azureContainerRegistry)'
- Dockerfile: 'src/Microsoft.Health.Dicom.Functions.App/Docker/Dockerfile'
- buildContext: '$(Build.Repository.LocalPath)'
- arguments: '--build-arg BUILD_CONFIGURATION=Release --build-arg CONTINUOUS_INTEGRATION_BUILD=true'
- repository: dicom-functions
- tags: ${{ parameters.tag }}
-
- - task: Docker@2
- displayName: 'Build dicom-uploader'
- inputs:
- command: 'build'
- containerRegistry: '$(azureContainerRegistry)'
- Dockerfile: 'tools/uploader-function/src/DicomUploaderFunction/Dockerfile'
- buildContext: '$(Build.Repository.LocalPath)'
- arguments: '--build-arg BUILD_CONFIGURATION=Release --build-arg CONTINUOUS_INTEGRATION_BUILD=true'
- repository: dicom-uploader
- tags: |
- ${{ parameters.tag }}
- latest
-
- # Build SQL for the sake of component governance
- - task: Docker@2
- displayName: 'Build SQL Server Image'
- inputs:
- command: 'build'
- containerRegistry: '$(azureContainerRegistry)'
- Dockerfile: 'docker/sql/Dockerfile'
- buildContext: '$(Build.Repository.LocalPath)'
- repository: mssql-server-focal
- tags: ${{ parameters.tag }}
-
- - task: ComponentGovernanceComponentDetection@0
- inputs:
- scanType: 'Register'
- verbosity: 'Verbose'
- alertWarningLevel: 'High'
- failOnAlert: true
-
- - ${{ if eq(parameters.push, true) }}:
- - task: Docker@2
- displayName: 'Push dicom-server'
- inputs:
- command: 'push'
- containerRegistry: '$(azureContainerRegistry)'
- repository: dicom-server
- tags: ${{ parameters.tag }}
-
- - ${{ if eq(parameters.push, true) }}:
- - task: Docker@2
- displayName: 'Push dicom-cast'
- inputs:
- command: 'push'
- containerRegistry: '$(azureContainerRegistry)'
- repository: dicom-cast
- tags: |
- ${{ parameters.tag }}
- latest
-
- - ${{ if eq(parameters.push, true) }}:
- - task: Docker@2
- displayName: 'Push dicom-functions'
- inputs:
- command: 'push'
- containerRegistry: '$(azureContainerRegistry)'
- repository: dicom-functions
- tags: ${{ parameters.tag }}
-
- - ${{ if eq(parameters.push, true) }}:
- - task: Docker@2
- displayName: 'Push dicom-uploader'
- inputs:
- command: 'push'
- containerRegistry: '$(azureContainerRegistry)'
- repository: dicom-uploader
- tags: |
- ${{ parameters.tag }}
- latest
-
- - ${{ if eq(parameters.push, true) }}:
- - task: Docker@2
- displayName: Logout
- inputs:
- command: logout
- containerRegistry: '$(azureContainerRegistry)'
- condition: always()
diff --git a/build/common/package-nugets.yml b/build/common/package-nugets.yml
deleted file mode 100644
index cb2afcf151..0000000000
--- a/build/common/package-nugets.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-steps:
- # Package nugets
- - task: DotNetCoreCLI@2
- displayName: 'dotnet pack nugets'
- inputs:
- command: pack
- configuration: '$(buildConfiguration)'
- packagesToPack: '**/*.csproj;!**/*.UnitTests.csproj'
- packDirectory: '$(build.artifactStagingDirectory)/nupkgs'
- versioningScheme: byEnvVar
- versionEnvVar: 'nuGetVersion'
- nobuild: true
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish nuget artifacts'
- inputs:
- pathtoPublish: '$(build.artifactStagingDirectory)/nupkgs'
- artifactName: 'nuget'
- publishLocation: 'container'
-
- - task: CopyFiles@2
- displayName: 'copy symbols'
- inputs:
- sourceFolder: '$(build.sourcesDirectory)'
- contents: |
- **/*.pdb
- !**/*.UnitTests.pdb
- targetFolder: '$(build.artifactStagingDirectory)/symbols'
- cleanTargetFolder: true
- flattenFolders: true
- overWrite: true
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish symbol artifacts'
- inputs:
- pathtoPublish: '$(build.artifactStagingDirectory)/symbols'
- artifactName: 'symbols'
- publishLocation: 'container'
diff --git a/build/common/package.yml b/build/common/package.yml
deleted file mode 100644
index 6b29fcbe92..0000000000
--- a/build/common/package.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-steps:
-
- # Package web
-
- - task: DotNetCoreCLI@2
- displayName: 'dotnet publish web'
- inputs:
- command: publish
- projects: '**/Microsoft.Health.Dicom.Web.csproj'
- arguments: '--configuration $(buildConfiguration) --output $(build.artifactStagingDirectory)/web --no-build'
- publishWebProjects: false
-
- - task: DotNetCoreCLI@2
- displayName: 'dotnet publish functions'
- inputs:
- command: publish
- projects: '**/Microsoft.Health.Dicom.Functions.App.csproj'
- arguments: '--configuration $(buildConfiguration) --output $(build.artifactStagingDirectory)/functions --no-build'
- publishWebProjects: false
-
- - task: DotNetCoreCLI@2
- displayName: 'dotnet publish Integration Tests'
- inputs:
- command: publish
- projects: 'test/**/*.csproj'
- arguments: '--configuration $(buildConfiguration) --output "$(build.binariesdirectory)/IntegrationTests" --no-build'
- publishWebProjects: false
- zipAfterPublish: false
-
- # Publish artifacts
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish web artifacts'
- inputs:
- pathToPublish: '$(build.artifactStagingDirectory)/web'
- artifactName: 'deploy'
- artifactType: 'container'
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish function artifacts'
- inputs:
- pathToPublish: '$(build.artifactStagingDirectory)/functions'
- artifactName: 'deploy'
- artifactType: 'container'
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish samples'
- inputs:
- pathToPublish: './samples/'
- artifactName: 'deploy'
- artifactType: 'container'
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish dicom-cast samples'
- inputs:
- pathToPublish: './converter/dicom-cast/samples/'
- artifactName: 'deploy-dicom-cast'
- artifactType: 'container'
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish global.json'
- inputs:
- pathToPublish: './global.json'
- artifactName: 'deploy'
- artifactType: 'container'
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish test configuration jsons'
- enabled: false
- inputs:
- pathToPublish: './test/configuration/'
- artifactName: 'deploy'
- artifactType: 'container'
-
- - task: PublishBuildArtifacts@1
- displayName: 'publish Integration Tests'
- inputs:
- pathToPublish: '$(build.binariesdirectory)/IntegrationTests'
- artifactName: 'IntegrationTests'
- artifactType: 'container'
diff --git a/build/common/run-e2e-connected-store-tests.yml b/build/common/run-e2e-connected-store-tests.yml
deleted file mode 100644
index 66c7c42e36..0000000000
--- a/build/common/run-e2e-connected-store-tests.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-parameters:
- externalStorageTests: true
-
-steps:
-- task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'IntegrationTests'
-
-- script: dotnet test "Microsoft.Health.Dicom.Web.Tests.E2E.dll" --filter "Category!=bvt-dp" --logger trx --results-directory "$(Agent.TempDirectory)/TestResults" -e DicomServer__Features__EnableExternalStore="true"
- displayName: 'dotnet test Microsoft.Health.Dicom.Web.Tests.E2E.dll with EnableExternalStore'
- workingDirectory: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Dicom.Web.Tests.E2E'
- condition: eq(${{ parameters.externalStorageTests }}, true)
-
-- task: PublishTestResults@2
- displayName: 'Publish Connected Store Test Results'
- condition: succeededOrFailed()
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '*.trx'
- searchFolder: '$(Agent.TempDirectory)/TestResults'
- testRunTitle: 'E2E Connected Store Tests'
- buildConfiguration: '$(buildConfiguration)'
diff --git a/build/common/run-e2e-features-enabled-connected-store-tests.yml b/build/common/run-e2e-features-enabled-connected-store-tests.yml
deleted file mode 100644
index 24766ec354..0000000000
--- a/build/common/run-e2e-features-enabled-connected-store-tests.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-steps:
-- task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'IntegrationTests'
-
-- script: dotnet test "Microsoft.Health.Dicom.Web.Tests.E2E.dll" --filter "Category=bvt-dp" --logger trx --results-directory "$(Agent.TempDirectory)/TestResults" -e DicomServer__Features__EnableExternalStore="true"
- displayName: 'dotnet test Microsoft.Health.Dicom.Web.Tests.E2E.dll with EnableExternalStore'
- workingDirectory: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Dicom.Web.Tests.E2E'
-
-- task: PublishTestResults@2
- displayName: 'Publish Connected Store Test Results'
- condition: succeededOrFailed()
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '*.trx'
- searchFolder: '$(Agent.TempDirectory)/TestResults'
- testRunTitle: 'Partitioned Connected Store E2E Tests'
- buildConfiguration: '$(buildConfiguration)'
diff --git a/build/common/run-e2e-features-enabled-tests.yml b/build/common/run-e2e-features-enabled-tests.yml
deleted file mode 100644
index 3334b6ae7a..0000000000
--- a/build/common/run-e2e-features-enabled-tests.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-steps:
-- task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'IntegrationTests'
-
-- script: dotnet test "Microsoft.Health.Dicom.Web.Tests.E2E.dll" --filter "Category=bvt-dp" --logger trx --results-directory "$(Agent.TempDirectory)/TestResults" -e DicomServer__Features__EnableExternalStore="false"
- displayName: 'dotnet test Microsoft.Health.Dicom.Web.Tests.E2E.dll'
- workingDirectory: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Dicom.Web.Tests.E2E'
-
-- task: PublishTestResults@2
- displayName: 'Publish Test Results'
- condition: succeededOrFailed()
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '*.trx'
- searchFolder: '$(Agent.TempDirectory)/TestResults'
- testRunTitle: 'Partitioned E2E Tests'
- buildConfiguration: '$(buildConfiguration)'
diff --git a/build/common/run-e2e-tests.yml b/build/common/run-e2e-tests.yml
deleted file mode 100644
index 0d4ad7653f..0000000000
--- a/build/common/run-e2e-tests.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-parameters:
- externalStorageTests: true
-
-steps:
-- task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'IntegrationTests'
-
-- script: dotnet test "Microsoft.Health.Dicom.Web.Tests.E2E.dll" --filter "Category!=bvt-dp" --logger trx --results-directory "$(Agent.TempDirectory)/TestResults" -e DicomServer__Features__EnableExternalStore="false"
- displayName: 'dotnet test Microsoft.Health.Dicom.Web.Tests.E2E.dll'
- workingDirectory: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Dicom.Web.Tests.E2E'
-
-- task: PublishTestResults@2
- displayName: 'Publish Test Results'
- condition: succeededOrFailed()
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '*.trx'
- searchFolder: '$(Agent.TempDirectory)/TestResults'
- testRunTitle: 'E2E Tests'
- buildConfiguration: '$(buildConfiguration)'
diff --git a/build/common/run-integration-tests.yml b/build/common/run-integration-tests.yml
deleted file mode 100644
index 2495d9103e..0000000000
--- a/build/common/run-integration-tests.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-steps:
-- task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'IntegrationTests'
-
-- script: dotnet test "Microsoft.Health.Dicom.Tests.Integration.dll" --logger trx --results-directory "$(Agent.TempDirectory)/TestResults"
- displayName: 'dotnet test Microsoft.Health.Dicom.Tests.Integration.dll'
- workingDirectory: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Dicom.Tests.Integration'
-
-- task: PublishTestResults@2
- displayName: 'Publish Test Results'
- condition: succeededOrFailed()
- inputs:
- testResultsFormat: 'VSTest'
- testResultsFiles: '*.trx'
- searchFolder: '$(Agent.TempDirectory)/TestResults'
- buildConfiguration: '$(buildConfiguration)'
diff --git a/build/common/scripts/CheckForBreakingAPISwaggerChanges.ps1 b/build/common/scripts/CheckForBreakingAPISwaggerChanges.ps1
deleted file mode 100644
index eae7cd6ada..0000000000
--- a/build/common/scripts/CheckForBreakingAPISwaggerChanges.ps1
+++ /dev/null
@@ -1,37 +0,0 @@
-<#
-.SYNOPSIS
-Generates the OpenApi doc for the specified version and compares it with the baseline to make sure no breaking changes are introduced
-Run script from root of this repository
-.Parameter SwaggerDir
-Swagger directory path from root of this repository. Ex: 'swagger'
-.PARAMETER Versions
-Api versions to generate the OpenApiDoc for and compare with baseline
-#>
-
-param(
- [string]$SwaggerDir,
-
- [String[]]$Versions
-)
-
-$ErrorActionPreference = 'Stop'
-$container="openapitools/openapi-diff:latest@sha256:442d61387d4d3c5bff282f1eb30decffa87c8c4acae77e6ac3f815b1f63672ea"
-
-if (Test-Path "$SwaggerDir/FromMain") { Remove-Item -Recurse -Force "$SwaggerDir/FromMain" }
-mkdir "$SwaggerDir/FromMain"
-
-foreach ($Version in $Versions)
-{
- $new=(Join-Path -Path "$SwaggerDir" -ChildPath "$Version/swagger.yaml")
- $old=(Join-Path -Path "$SwaggerDir" -ChildPath "/FromMain/$Version.yaml")
-
- $SwaggerOnMain="https://raw.githubusercontent.com/microsoft/dicom-server/main/swagger/$Version/swagger.yaml"
- Invoke-WebRequest -Uri $SwaggerOnMain -OutFile $old
-
- write-host "Running comparison with baseline for version $Version"
- Write-Host "old: $old"
- Write-Host "new: $new"
- docker run --rm -t -v "${pwd}/${SwaggerDir}:/${SwaggerDir}:ro" $container /$old /$new --fail-on-incompatible
-}
-
-Remove-Item -Recurse -Force "$SwaggerDir/FromMain"
diff --git a/build/common/scripts/CheckForSwaggerChanges.ps1 b/build/common/scripts/CheckForSwaggerChanges.ps1
deleted file mode 100644
index a9ca4bad5b..0000000000
--- a/build/common/scripts/CheckForSwaggerChanges.ps1
+++ /dev/null
@@ -1,52 +0,0 @@
-<#
-.SYNOPSIS
-Generates the OpenApi doc for the specified version and compares it with the checked in version to ensure it is up to date.
-.Parameter SwaggerDir
-The working directory
-.PARAMETER AssemblyDir
-Path for the web projects dll
-.PARAMETER Versions
-Api versions to compare with
-#>
-
-param(
- [string]$SwaggerDir,
-
- [string]$AssemblyDir,
-
- [String[]]$Versions
-)
-
-$ErrorActionPreference = 'Stop' # ensure script behaves same locally as within default pwsh ado task
-dotnet tool restore
-
-Write-Host "Using swagger version ..."
-dotnet tool list | Select-String "swashbuckle"
-
-Write-Host "Testing that swagger will work ..."
-dotnet swagger
-
-foreach ($Version in $Versions)
-{
- Write-Host "Ensuring directory path exists for swagger api version $Version"
- $ProjectSwaggerDir=".\swagger\$Version"
- if (!(Test-Path $ProjectSwaggerDir -PathType Container)) {
- New-Item -ItemType Directory -Force -Path $ProjectSwaggerDir
- Write-Host "Directory $ProjectSwaggerDir did not exist. Directory created."
- }
-
- $WritePath=(Join-Path -Path "$SwaggerDir" -ChildPath "$Version.yaml")
- Write-Host "Generating swagger yaml file for $Version to path $WritePath"
-
- dotnet swagger tofile --yaml --output $WritePath "$AssemblyDir" $Version
-
- Write-Host "Comparing generated swagger with what was checked in ..."
- $HasDifferences = (Compare-Object -ReferenceObject (Get-Content -Path $WritePath) -DifferenceObject (Get-Content -Path "$ProjectSwaggerDir\swagger.yaml"))
-
- if ($HasDifferences){
- Write-Host $HasDifferences
- throw "The swagger yaml checked in with this PR is not up to date with code. Please build the sln, which will trigger a hook to autogenerate these files on your behalf. Differences shown above."
- } else{
- Write-Host "Swagger checked in with this PR is up to date with code."
- }
-}
diff --git a/build/common/update-semver.yml b/build/common/update-semver.yml
deleted file mode 100644
index c4df95ae82..0000000000
--- a/build/common/update-semver.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-steps:
-- task: gitversion/setup@0
- displayName: 'Setup GitVersion'
- inputs:
- versionSpec: '5.x'
-
-# All variables from the GitVersion task are prefixed by "GitVersion." (eg. GitVersion.SemVer)
-- task: gitversion/execute@0
- name: 'DicomVersion'
- displayName: 'Run GitVersion'
- inputs:
- configFilePath: 'GitVersion.yml'
- targetPath: '$(Build.SourcesDirectory)'
- useConfigFile: true
diff --git a/build/common/versioning.yml b/build/common/versioning.yml
deleted file mode 100644
index 4fbab300f4..0000000000
--- a/build/common/versioning.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-jobs:
-- job: OpenApiDiff
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .NET 7 sdk'
- inputs:
- version: 7.x
-
- - task: UseDotNet@2
- displayName: 'Use .NET sdk'
- inputs:
- useGlobalJson: true
-
- - task: DownloadBuildArtifacts@0
- inputs:
- buildType: 'current'
- downloadType: 'single'
- downloadPath: '$(System.ArtifactsDirectory)'
- artifactName: 'deploy'
-
- - task: ExtractFiles@1
- displayName: 'Extract Web zip'
- inputs:
- archiveFilePatterns: '$(System.ArtifactsDirectory)/deploy/Microsoft.Health.Dicom.Web.zip'
- destinationFolder: '$(System.ArtifactsDirectory)/deploy/webArtifacts'
-
- - task: PowerShell@2
- displayName: 'Check for latest Swagger changes'
- inputs:
- pwsh: true
- filepath: './build/common/scripts/CheckForSwaggerChanges.ps1'
- arguments: >
- -SwaggerDir '$(System.DefaultWorkingDirectory)/swagger'
- -AssemblyDir '$(System.ArtifactsDirectory)/deploy/webArtifacts/Microsoft.Health.Dicom.Web.dll'
- -Version 'v1-prerelease','v1','v2'
-
- - task: PowerShell@2
- displayName: 'Check for breaking API / Swagger changes'
- enabled: false # will re-enable once v2 swagger exists on main
- inputs:
- pwsh: true
- filepath: './build/common/scripts/CheckForBreakingAPISwaggerChanges.ps1'
- arguments: >
- -SwaggerDir 'swagger'
- -Version 'v1-prerelease','v1','v2'
diff --git a/build/pr/run-e2e-features-enabled-tests.yml b/build/pr/run-e2e-features-enabled-tests.yml
deleted file mode 100644
index 49dab0c2e1..0000000000
--- a/build/pr/run-e2e-features-enabled-tests.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-jobs:
-- job: SetupAndRun
- displayName: 'Feature-Specific E2E Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - script: ContinuousIntegrationBuild=true docker-compose -p healthcare -f docker-compose.yml -f docker-compose.features.yml up --build -d
- displayName: 'Run docker-compose'
- workingDirectory: 'docker'
-
- - bash: for i in {1..12}; do curl -fsS "$(testEnvironmentUrl)health/check" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Server'
-
- - bash: for i in {1..12}; do curl -fsS "$(testFunctionsUrl)" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Functions'
-
- - template: ../common/run-e2e-features-enabled-tests.yml
-
- - script: docker-compose -p healthcare -f docker-compose.yml -f docker-compose.features.yml logs -t
- displayName: 'docker-compose logs'
- workingDirectory: 'docker'
- condition: always()
-
- - script: docker-compose -p healthcare -f docker-compose.yml -f docker-compose.features.yml rm -s -f
- displayName: 'Stop docker-compose'
- workingDirectory: 'docker'
- condition: always()
-
-- job: SetupAndRunConnectedStore
- displayName: 'Feature-Specific E2E Connected Store Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - script: ContinuousIntegrationBuild=true docker-compose -p healthcare -f docker-compose.yml -f docker-compose.features.yml up --build -d
- displayName: 'Run docker-compose'
- workingDirectory: 'docker'
-
- - bash: for i in {1..12}; do curl -fsS "$(testEnvironmentUrl)health/check" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Server'
-
- - bash: for i in {1..12}; do curl -fsS "$(testFunctionsUrl)" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Functions'
-
- - template: ../common/run-e2e-features-enabled-connected-store-tests.yml
-
- - script: docker-compose -p healthcare -f docker-compose.yml -f docker-compose.features.yml logs -t
- displayName: 'docker-compose logs'
- workingDirectory: 'docker'
- condition: always()
-
- - script: docker-compose -p healthcare -f docker-compose.yml -f docker-compose.features.yml rm -s -f
- displayName: 'Stop docker-compose'
- workingDirectory: 'docker'
- condition: always()
\ No newline at end of file
diff --git a/build/pr/run-e2e-tests.yml b/build/pr/run-e2e-tests.yml
deleted file mode 100644
index 33d5f81f08..0000000000
--- a/build/pr/run-e2e-tests.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-jobs:
-- job: SetupAndRun
- displayName: 'E2E Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - script: ContinuousIntegrationBuild=true docker-compose -p healthcare -f docker-compose.yml -f docker-compose.ports.azurite.yml up --build -d
- displayName: 'Run docker-compose'
- workingDirectory: 'docker'
-
- - bash: for i in {1..12}; do curl -fsS "$(testEnvironmentUrl)health/check" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Server'
-
- - bash: for i in {1..12}; do curl -fsS "$(testFunctionsUrl)" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Functions'
-
- - template: ../common/run-e2e-tests.yml
-
- - script: docker-compose -p healthcare -f docker-compose.yml logs -t
- displayName: 'docker-compose logs'
- workingDirectory: 'docker'
- condition: always()
-
- - script: docker-compose -p healthcare -f docker-compose.yml rm -s -f
- displayName: 'Stop docker-compose'
- workingDirectory: 'docker'
- condition: always()
-
-- job: SetupAndRunConnectedStore
- displayName: 'E2E Connected Store Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - script: ContinuousIntegrationBuild=true docker-compose -p healthcare -f docker-compose.yml -f docker-compose.ports.azurite.yml up --build -d
- displayName: 'Run docker-compose'
- workingDirectory: 'docker'
-
- - bash: for i in {1..12}; do curl -fsS "$(testEnvironmentUrl)health/check" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Server'
-
- - bash: for i in {1..12}; do curl -fsS "$(testFunctionsUrl)" > /dev/null && exit 0 || sleep 5; done; exit 1
- displayName: 'Wait for DICOM Functions'
-
- - template: ../common/run-e2e-connected-store-tests.yml
-
- - script: docker-compose -p healthcare -f docker-compose.yml logs -t
- displayName: 'docker-compose logs'
- workingDirectory: 'docker'
- condition: always()
-
- - script: docker-compose -p healthcare -f docker-compose.yml rm -s -f
- displayName: 'Stop docker-compose'
- workingDirectory: 'docker'
- condition: always()
\ No newline at end of file
diff --git a/build/pr/run-integration-tests.yml b/build/pr/run-integration-tests.yml
deleted file mode 100644
index 257ce29d9d..0000000000
--- a/build/pr/run-integration-tests.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-jobs:
-- job: SetupAndRun
- displayName: 'Integration Tests'
- pool:
- vmImage: 'ubuntu-latest'
- steps:
- - task: UseDotNet@2
- displayName: 'Use .Net Core sdk'
- inputs:
- useGlobalJson: true
-
- - bash: |
- # Set Environment Variables
- echo "##vso[task.setvariable variable=BlobStore__ConnectionString]UseDevelopmentStorage=true"
- echo "##vso[task.setvariable variable=SqlServer__ConnectionString]Server=(local);Persist Security Info=False;User ID=sa;Password=L0ca1P@ssw0rd;MultipleActiveResultSets=False;Connection Timeout=30;TrustServerCertificate=true"
-
- # Start Azurite
- docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 -d --rm --name azure-storage mcr.microsoft.com/azure-storage/azurite:latest
-
- # Start SQL Server
- docker build -t fulltext-mssql -f "docker/sql/Dockerfile" .
- docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=L0ca1P@ssw0rd" -p 1433:1433 -d --rm --name sql-server fulltext-mssql
-
- # Wait for SQL to start
- for i in {1..6}; do docker exec sql-server sh -c "/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P L0ca1P@ssw0rd -Q 'SELECT * FROM INFORMATION_SCHEMA.TABLES'" && exit 0 || sleep 5; done; exit 1
- displayName: 'Start Docker Dependencies'
-
- - template: ../common/run-integration-tests.yml
-
- - script: |
- docker stop azure-storage
- docker stop sql-server
- displayName: 'Stop Docker Dependencies'
- condition: always()
diff --git a/build/pr/variables.yml b/build/pr/variables.yml
deleted file mode 100644
index 843de44108..0000000000
--- a/build/pr/variables.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-variables:
- prNumber: $(system.pullRequest.pullRequestNumber)
- deploymentName: 'msh-dicom-pr-$(prNumber)'
- testEnvironmentUrl: 'http://localhost:8080/'
- testFunctionsUrl: 'http://localhost:7072/'
- resourceGroupName: $(deploymentName)
- resourceGroupRegion: 'southcentralus'
- appServicePlanResourceGroup: 'msh-dicom-pr'
- azureSubscriptionId: 'a1766500-6fd5-4f5c-8515-607798271014'
- azureSubscriptionName: 'Dicom OSS'
- azureContainerRegistry: 'dicomoss.azurecr.io'
- buildConfiguration: 'Release'
- imageTag: '$(build.BuildNumber)'
- skipNugetSecurityAnalysis: 'true' # NuGet config contains multiple feeds but meets exception criteria
- Tests__Export__Sink__ConnectionString: 'DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;'
diff --git a/converter/dicom-cast/Microsoft.Health.DicomCast.sln b/converter/dicom-cast/Microsoft.Health.DicomCast.sln
deleted file mode 100644
index c22619f085..0000000000
--- a/converter/dicom-cast/Microsoft.Health.DicomCast.sln
+++ /dev/null
@@ -1,82 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.2.32414.248
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4EB555C8-F221-4663-B314-58AF0331A225}"
- ProjectSection(SolutionItems) = preProject
- ..\..\.editorconfig = ..\..\.editorconfig
- ..\..\.globalconfig = ..\..\.globalconfig
- ..\..\Directory.Build.props = ..\..\Directory.Build.props
- ..\..\Directory.Packages.props = ..\..\Directory.Packages.props
- ..\..\GitVersion.yml = ..\..\GitVersion.yml
- ..\..\global.json = ..\..\global.json
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E15E21A1-0707-4D98-AEDD-ACC7F59C5681}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.DicomCast.Core", "src\Microsoft.Health.DicomCast.Core\Microsoft.Health.DicomCast.Core.csproj", "{AAD757E8-D347-4006-BC43-11D761B04078}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.DicomCast.Hosting", "src\Microsoft.Health.DicomCast.Hosting\Microsoft.Health.DicomCast.Hosting.csproj", "{3D4D8CBB-B32F-4BB3-98B9-39F457C71E92}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.DicomCast.Core.UnitTests", "src\Microsoft.Health.DicomCast.Core.UnitTests\Microsoft.Health.DicomCast.Core.UnitTests.csproj", "{E60031C2-46AB-40E3-BDD9-A8F28A13CF32}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.Dicom.Client", "..\..\src\Microsoft.Health.Dicom.Client\Microsoft.Health.Dicom.Client.csproj", "{3CCE3438-139A-420A-8A60-1144CADA95A1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.DicomCast.TableStorage.UnitTests", "src\Microsoft.Health.DicomCast.TableStorage.UnitTests\Microsoft.Health.DicomCast.TableStorage.UnitTests.csproj", "{1444027E-07DA-412E-8E74-F077F5520530}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.DicomCast.TableStorage", "src\Microsoft.Health.DicomCast.TableStorage\Microsoft.Health.DicomCast.TableStorage.csproj", "{0DFB24D5-5E2F-435D-A435-F70CD1824F08}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Health.FellowOakDicom", "..\..\forks\Microsoft.Health.FellowOakDicom\Microsoft.Health.FellowOakDicom.csproj", "{972888D8-AFAE-4F88-9405-2B66CE70C41F}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x64 = Debug|x64
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {AAD757E8-D347-4006-BC43-11D761B04078}.Debug|x64.ActiveCfg = Debug|x64
- {AAD757E8-D347-4006-BC43-11D761B04078}.Debug|x64.Build.0 = Debug|x64
- {AAD757E8-D347-4006-BC43-11D761B04078}.Release|x64.ActiveCfg = Release|x64
- {AAD757E8-D347-4006-BC43-11D761B04078}.Release|x64.Build.0 = Release|x64
- {3D4D8CBB-B32F-4BB3-98B9-39F457C71E92}.Debug|x64.ActiveCfg = Debug|x64
- {3D4D8CBB-B32F-4BB3-98B9-39F457C71E92}.Debug|x64.Build.0 = Debug|x64
- {3D4D8CBB-B32F-4BB3-98B9-39F457C71E92}.Release|x64.ActiveCfg = Release|x64
- {3D4D8CBB-B32F-4BB3-98B9-39F457C71E92}.Release|x64.Build.0 = Release|x64
- {E60031C2-46AB-40E3-BDD9-A8F28A13CF32}.Debug|x64.ActiveCfg = Debug|x64
- {E60031C2-46AB-40E3-BDD9-A8F28A13CF32}.Debug|x64.Build.0 = Debug|x64
- {E60031C2-46AB-40E3-BDD9-A8F28A13CF32}.Release|x64.ActiveCfg = Release|x64
- {E60031C2-46AB-40E3-BDD9-A8F28A13CF32}.Release|x64.Build.0 = Release|x64
- {3CCE3438-139A-420A-8A60-1144CADA95A1}.Debug|x64.ActiveCfg = Debug|x64
- {3CCE3438-139A-420A-8A60-1144CADA95A1}.Debug|x64.Build.0 = Debug|x64
- {3CCE3438-139A-420A-8A60-1144CADA95A1}.Release|x64.ActiveCfg = Release|x64
- {3CCE3438-139A-420A-8A60-1144CADA95A1}.Release|x64.Build.0 = Release|x64
- {1444027E-07DA-412E-8E74-F077F5520530}.Debug|x64.ActiveCfg = Debug|x64
- {1444027E-07DA-412E-8E74-F077F5520530}.Debug|x64.Build.0 = Debug|x64
- {1444027E-07DA-412E-8E74-F077F5520530}.Release|x64.ActiveCfg = Release|x64
- {1444027E-07DA-412E-8E74-F077F5520530}.Release|x64.Build.0 = Release|x64
- {0DFB24D5-5E2F-435D-A435-F70CD1824F08}.Debug|x64.ActiveCfg = Debug|x64
- {0DFB24D5-5E2F-435D-A435-F70CD1824F08}.Debug|x64.Build.0 = Debug|x64
- {0DFB24D5-5E2F-435D-A435-F70CD1824F08}.Release|x64.ActiveCfg = Release|x64
- {0DFB24D5-5E2F-435D-A435-F70CD1824F08}.Release|x64.Build.0 = Release|x64
- {972888D8-AFAE-4F88-9405-2B66CE70C41F}.Debug|x64.ActiveCfg = Debug|x64
- {972888D8-AFAE-4F88-9405-2B66CE70C41F}.Debug|x64.Build.0 = Debug|x64
- {972888D8-AFAE-4F88-9405-2B66CE70C41F}.Release|x64.ActiveCfg = Release|x64
- {972888D8-AFAE-4F88-9405-2B66CE70C41F}.Release|x64.Build.0 = Release|x64
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {AAD757E8-D347-4006-BC43-11D761B04078} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- {3D4D8CBB-B32F-4BB3-98B9-39F457C71E92} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- {E60031C2-46AB-40E3-BDD9-A8F28A13CF32} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- {3CCE3438-139A-420A-8A60-1144CADA95A1} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- {1444027E-07DA-412E-8E74-F077F5520530} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- {0DFB24D5-5E2F-435D-A435-F70CD1824F08} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- {972888D8-AFAE-4F88-9405-2B66CE70C41F} = {E15E21A1-0707-4D98-AEDD-ACC7F59C5681}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {4EF00533-B2AE-4B1B-B4AF-9799AD9F69E2}
- EndGlobalSection
-EndGlobal
diff --git a/converter/dicom-cast/docs/DicomcastDeploymentTemplate.md b/converter/dicom-cast/docs/DicomcastDeploymentTemplate.md
deleted file mode 100644
index 21f3174d30..0000000000
--- a/converter/dicom-cast/docs/DicomcastDeploymentTemplate.md
+++ /dev/null
@@ -1,340 +0,0 @@
-```json
-{
- "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "parameters": {
- "serviceName": {
- "minLength": 3,
- "maxLength": 24,
- "type": "String",
- "metadata": {
- "description": "Name of the DICOM Cast service container group."
- }
- },
- "image": {
- "defaultValue": "dicomoss.azurecr.io/dicom-cast",
- "type": "String",
- "metadata": {
- "description": "Container image to deploy. Should be of the form repoName/imagename:tag for images stored in public Docker Hub, or a fully qualified URI for other registries. Images from private registries require additional registry credentials."
- }
- },
- "storageAccountSku": {
- "defaultValue": "Standard_LRS",
- "allowedValues": [
- "Standard_LRS",
- "Standard_GRS",
- "Standard_RAGRS",
- "Standard_ZRS",
- "Premium_LRS",
- "Premium_ZRS",
- "Standard_GZRS",
- "Standard_RAGZRS"
- ],
- "type": "String"
- },
- "deployApplicationInsights": {
- "defaultValue": true,
- "type": "Bool",
- "metadata": {
- "description": "Deploy Application Insights for the DICOM server. Disabled for Microsoft Azure Government (MAG)"
- }
- },
- "applicationInsightsLocation": {
- "defaultValue": "[resourceGroup().location]",
- "allowedValues": [
- "southeastasia",
- "northeurope",
- "westeurope",
- "eastus",
- "southcentralus",
- "westus2"
- ],
- "type": "String"
- },
- "cpuCores": {
- "defaultValue": "1.0",
- "type": "String",
- "metadata": {
- "description": "The number of CPU cores to allocate to the container."
- }
- },
- "memoryInGb": {
- "defaultValue": "1.5",
- "type": "String",
- "metadata": {
- "description": "The amount of memory to allocate to the container in gigabytes."
- }
- },
- "location": {
- "defaultValue": "[resourceGroup().location]",
- "type": "String",
- "metadata": {
- "description": "Location for all resources."
- }
- },
- "restartPolicy": {
- "defaultValue": "always",
- "allowedValues": [
- "never",
- "always",
- "onfailure"
- ],
- "type": "String",
- "metadata": {
- "description": "The behavior of Azure runtime if container has stopped."
- }
- },
- "dicomWebEndpoint": {
- "type": "String",
- "metadata": {
- "description": "The endpoint of the DICOM Web server."
- }
- },
- "fhirEndpoint": {
- "type": "String",
- "metadata": {
- "description": "The endpoint of the FHIR server."
- }
- },
- "patientSystemId": {
- "type": "String",
- "metadata": {
- "description": "Patient SystemId configured by the user"
- }
- },
- "isIssuerIdUsed": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Issuer id or patient system id used based on this boolean value"
- }
- },
- "enforceValidationOfTagValues": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Enforce validation of all tag values and do not store to FHIR even if only non-required tags are invalid"
- }
- },
- "ignoreJsonParsingErrors": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Ignore json parsing errors for DICOM instances with malformed DICOM json"
- }
- },
- "additionalEnvironmentVariables": {
- "defaultValue": [],
- "type": "Array",
- "metadata": {
- "description": "Array of additional enviornment variables with objects with properties 'name' and 'value'. ex: [{\"name\": \"testName\", \"value\": \"testValue\"}]"
- }
- },
- "virtualNetworkName": {
- "type": "String",
- "metadata": {
- "description": "Virtual network where ACi will be deployed to"
- }
- },
- "subnetName": {
- "type": "String",
- "metadata": {
- "description": "Subnet within a virtual network which is delegated to ACI"
- }
- }
- },
- "variables": {
- "networkProfileName": "aci-networkProfile",
- "interfaceConfigName": "eth0",
- "interfaceIpConfig": "ipconfigprofile1",
- "isMAG": "[or(contains(resourceGroup().location,'usgov'),contains(resourceGroup().location,'usdod'))]",
- "serviceName": "[toLower(parameters('serviceName'))]",
- "virtualNetworkName": "[parameters('virtualNetworkName')]",
- "subnetName": "[parameters('subnetName')]",
- "keyvaultName": "[concat(substring(replace(variables('serviceName'), '-', ''), 0, min(11, length(variables('serviceName')))), uniquestring(resourceGroup().id))]",
- "containerGroupResourceId": "[resourceId('Microsoft.ContainerInstance/containerGroups/', variables('serviceName'))]",
- "deployAppInsights": "[and(parameters('deployApplicationInsights'),not(variables('isMAG')))]",
- "appInsightsName": "[concat('AppInsights-', variables('serviceName'))]",
- "storageAccountName": "[concat(substring(replace(variables('serviceName'), '-', ''), 0, min(11, length(variables('serviceName')))), uniquestring(resourceGroup().id))]",
- "storageResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
- "keyVaultEndpoint": "[if(variables('isMAG'), concat('https://', variables('keyvaultName'), '.vault.usgovcloudapi.net/'), concat('https://', variables('keyvaultName'), '.vault.azure.net/'))]",
- "keyVaultResourceId": "[resourceId('Microsoft.KeyVault/vaults', variables('keyvaultName'))]",
- "networkProfileResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), variables('subnetName'))]",
- "environmentVariables": [
- {
- "name": "Fhir__Endpoint",
- "value": "[parameters('fhirEndpoint')]"
- },
- {
- "name": "DicomWeb__Endpoint",
- "value": "[parameters('dicomWebEndpoint')]"
- },
- {
- "name": "KeyVault__Endpoint",
- "value": "[variables('keyVaultEndpoint')]"
- },
- {
- "name": "DicomCast__Features__EnforceValidationOfTagValues",
- "value": "[parameters('enforceValidationOfTagValues')]"
- },
- {
- "name": "DicomCast__Features__IgnoreJsonParsingErrors",
- "value": "[parameters('ignoreJsonParsingErrors')]"
- },
- {
- "name": "Patient__PatientSystemId",
- "value": "[parameters('patientSystemId')]"
- },
- {
- "name": "Patient__IsIssuerIdUsed",
- "value": "[parameters('isIssuerIdUsed')]"
- }
- ]
- },
- "resources": [
- {
- "type": "Microsoft.Network/networkProfiles",
- "apiVersion": "2020-11-01",
- "name": "[variables('serviceName')]",
- "location": "[parameters('location')]",
- "properties": {
- "containerNetworkInterfaceConfigurations": [
- {
- "name": "[variables('interfaceConfigName')]",
- "properties": {
- "ipConfigurations": [
- {
- "name": "[variables('interfaceIpConfig')]",
- "properties": {
- "subnet": {
- "id": "[variables('networkProfileResourceId')]"
- }
- }
- }
- ]
- }
- }
- ]
- }
- },
- {
- "type": "Microsoft.ContainerInstance/containerGroups",
- "apiVersion": "2018-10-01",
- "name": "[variables('serviceName')]",
- "location": "[parameters('location')]",
- "dependsOn": [
- "[concat('Microsoft.Insights/components/', variables('appInsightsName'))]"
- ],
- "identity": {
- "type": "SystemAssigned"
- },
- "properties": {
- "containers": [
- {
- "name": "[variables('serviceName')]",
- "properties": {
- "image": "[parameters('image')]",
- "resources": {
- "requests": {
- "cpu": "[parameters('cpuCores')]",
- "memoryInGb": "[parameters('memoryInGb')]"
- }
- },
- "environmentVariables": "[concat(variables('environmentVariables'), parameters('additionalEnvironmentVariables'), array(createObject('name', 'ApplicationInsights__InstrumentationKey', 'value', if(variables('deployAppInsights'), reference(concat('Microsoft.Insights/components/', variables('appInsightsName'))).InstrumentationKey, ''))))]"
- }
- }
- ],
- "osType": "Linux",
- "networkProfile": {
- "id": "[resourceId('Microsoft.Network/networkProfiles', variables('serviceName'))]"
- },
- "restartPolicy": "[parameters('restartPolicy')]"
- }
- },
- {
- "type": "Microsoft.Insights/components",
- "apiVersion": "2015-05-01",
- "name": "[variables('appInsightsName')]",
- "location": "[parameters('applicationInsightsLocation')]",
- "tags": {
- "[concat('hidden-link:', variables('containerGroupResourceId'))]": "Resource",
- "displayName": "AppInsightsComponent"
- },
- "kind": "web",
- "properties": {
- "Application_Type": "web",
- "ApplicationId": "[variables('serviceName')]"
- },
- "condition": "[variables('deployAppInsights')]"
- },
- {
- "type": "Microsoft.Storage/storageAccounts",
- "apiVersion": "2019-04-01",
- "name": "[variables('storageAccountName')]",
- "location": "[resourceGroup().location]",
- "tags": {},
- "sku": {
- "name": "[parameters('storageAccountSku')]"
- },
- "kind": "StorageV2",
- "properties": {
- "accessTier": "Hot",
- "supportsHttpsTrafficOnly": "true"
- }
- },
- {
- "type": "Microsoft.KeyVault/vaults",
- "apiVersion": "2015-06-01",
- "name": "[variables('keyvaultName')]",
- "location": "[resourceGroup().location]",
- "dependsOn": [
- "[variables('containerGroupResourceId')]"
- ],
- "tags": {},
- "properties": {
- "sku": {
- "family": "A",
- "name": "Standard"
- },
- "tenantId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]",
- "accessPolicies": [
- {
- "tenantId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]",
- "objectId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.principalId]",
- "permissions": {
- "secrets": [
- "get",
- "list",
- "set"
- ]
- }
- }
- ],
- "enabledForDeployment": false
- }
- },
- {
- "type": "Microsoft.KeyVault/vaults/secrets",
- "apiVersion": "2015-06-01",
- "name": "[concat(variables('keyvaultName'), '/TableStore--ConnectionString')]",
- "dependsOn": [
- "[variables('keyVaultResourceId')]",
- "[variables('storageResourceId')]"
- ],
- "properties": {
- "contentType": "text/plain",
- "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageResourceId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value, ';')]"
- }
- }
- ],
- "outputs": {
- "containerTenantId": {
- "type": "String",
- "value": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]"
- },
- "containerPrincipalId": {
- "type": "String",
- "value": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.principalId]"
- }
- }
-}
diff --git a/converter/dicom-cast/docs/authentication.md b/converter/dicom-cast/docs/authentication.md
deleted file mode 100644
index 4a4d206313..0000000000
--- a/converter/dicom-cast/docs/authentication.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# Authentication
-
-The DICOM cast project supports connecting to both DICOM and FHIR servers that require authentication. Currently there are three types of authentication supported for both servers. The authentication can be configured via the application settings by the appropriate values in the `Authentication` property of the given server.
-
-## Managed Identity
-
-This option uses the identity of the deployed DICOM cast instance to communicate with the server.
-
-```json
-{
- "DicomWeb": {
- "Endpoint": "https://dicom-server.example.com",
- "Authentication": {
- "Enabled": true,
- "AuthenticationType": "ManagedIdentity",
- "ManagedIdentityCredential": {
- "Resource": "https://dicom-server.example.com/"
- }
- }
- }
-}
-```
-
-## OAuth2 Client Credential
-
-This option uses a `client_credentials` OAuth2 grant to obtain an identity to communicate with the server.
-
-```json
-{
- "DicomWeb": {
- "Endpoint": "https://dicom-server.example.com",
- "Authentication": {
- "Enabled": true,
- "AuthenticationType": "OAuth2ClientCredential",
- "OAuth2ClientCredential": {
- "TokenUri": "https://idp.example.com/connect/token",
- "Resource": "https://dicom-server.example.com",
- "Scope": "https://dicom-server.example.com",
- "ClientId": "bdba742b-8138-4b7c-a6d8-03cbb7a8c053",
- "ClientSecret": "d8147077-d907-4551-8f40-90c6e86f3f0e"
- }
- }
- }
-}
-```
-
-## OAuth2 User Password
-
-This option uses a `password` OAuth2 grant to obtain an identity to communicate with the server.
-
-```json
-{
- "DicomWeb": {
- "Endpoint": "https://dicom-server.example.com",
- "Authentication": {
- "Enabled": true,
- "AuthenticationType": "OAuth2UserPasswordCredential",
- "OAuth2ClientCredential": {
- "TokenUri": "https://idp.example.com/connect/token",
- "Resource": "https://dicom-server.example.com",
- "Scope": "https://dicom-server.example.com",
- "ClientId": "bdba742b-8138-4b7c-a6d8-03cbb7a8c053",
- "ClientSecret": "d8147077-d907-4551-8f40-90c6e86f3f0e",
- "Username": "user@example.com",
- "Password": "randomstring"
- }
- }
- }
-}
-```
-
-## Secrets Management
-
-There are currently two ways provided to store secrets within the application.
-
-### User-Secrets
-
-User secrets are enabled when the `EnvironmentName` is `Development`. You can read more about the use of user secrets in [Safe storage of app secrets in development in ASP.NET Core](https://docs.microsoft.com/aspnet/core/security/app-secrets?view=aspnetcore-3.1).
-
-### KeyVault
-
-Using KeyVault to store secrets can be enabled by entering a value into the `KeyVault:Endpoint` configuration. On application start this will use the [current identity of the application](https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1#use-managed-identities-for-azure-resources) to read the key vault and add a configuration provider.
-
-Below is an example of the settings need to be added to the KeyVault for OAuth2ClientCredential authentication:
-
-* Add secrets related to Authentication in KeyVault for Medical Imaging Server for DICOM.
- + Example: If Medical Imaging Server for Azure was configured with `OAuth2ClientCredential`, below is the list of secrets that need to added to the KeyVault.
- - DicomWeb--Authentication--Enabled : True
- - DicomWeb--Authentication--AuthenticationType : OAuth2ClientCredential
- - DicomWeb--Authentication--OAuth2ClientCredential--TokenUri : ``````
- - DicomWeb--Authentication--OAuth2ClientCredential--Resource : ```Application ID URI of the resource app```
- - DicomWeb--Authentication--OAuth2ClientCredential--Scope : ```Application ID URI of the resource app```
- - DicomWeb--Authentication--OAuth2ClientCredential--ClientId : ```Client Id of the client app```
- - DicomWeb--Authentication--OAuth2ClientCredential--ClientSecret : ```Client app secret```
-* Add similar secrets to KeyVault for FHIR™ server.
-* Stop and Start the Container, to pickup the new configurations.
diff --git a/converter/dicom-cast/docs/image-1.png b/converter/dicom-cast/docs/image-1.png
deleted file mode 100644
index c79ecc4284..0000000000
Binary files a/converter/dicom-cast/docs/image-1.png and /dev/null differ
diff --git a/converter/dicom-cast/docs/image-2.png b/converter/dicom-cast/docs/image-2.png
deleted file mode 100644
index 73be295abd..0000000000
Binary files a/converter/dicom-cast/docs/image-2.png and /dev/null differ
diff --git a/converter/dicom-cast/docs/image-3.png b/converter/dicom-cast/docs/image-3.png
deleted file mode 100644
index 01d6e9cf3d..0000000000
Binary files a/converter/dicom-cast/docs/image-3.png and /dev/null differ
diff --git a/converter/dicom-cast/docs/image.png b/converter/dicom-cast/docs/image.png
deleted file mode 100644
index 2dad420f37..0000000000
Binary files a/converter/dicom-cast/docs/image.png and /dev/null differ
diff --git a/converter/dicom-cast/docs/workingWithPrivateLink.md b/converter/dicom-cast/docs/workingWithPrivateLink.md
deleted file mode 100644
index 1a1cc42dae..0000000000
--- a/converter/dicom-cast/docs/workingWithPrivateLink.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Configuration steps for DICOM Cast to work with Private Link enabled DICOM
-
-1. Create a Virtual Network with two subnets within the same subscription and region as you would plan to create the Health Data Services Workspace.
- 1. Default subnet
- 2. Subnet delegated to Microsoft.containerinstance/containergroups
-![Alt text](image.png)
-
-2. Provision Health Data Services workspace, DICOM and FHIR in the same region.
-3. Enable Private Link to Health Data Service Workspace. This Private Link would use the Virtual Network created in step 1 and default subnet
-![Alt text](image-1.png)
-
-4. Use the template given [here](DicomcastDeploymentTemplate.md) to deploy DICOM Cast within a Virtual Network created in step 1. This will use the subnet that is delegated to Microsoft.containerinstance/containergroups as shown in picture in step 1.
-5. Add the following role assignments to Health Data Service Workspace on container instances System Assigned Managed Identity.
- 1. DICOM Data Owner
- 2. FHIR Data Contributor
-![Alt text](image-2.png)
-
-6. DICOM Cast needs to talk to table storage within the storage account for processing Change Feed. Enable Private Link to the storage account created as a part of template deployment in step 4. This Private Link should also be in same vnet and default subnet that is created in step 1.
-![Alt text](image-3.png)
-
-7. Disable public network access for the storage account (`Security + Networking` > `Networking` > `Firewalls and virtual networks` > `Public Network Acccess` > `Disabled`)
-
-8. Ensure all the fields mentioned here are populated in the key vault provisioned in step 4. https://github.com/microsoft/dicom-server/blob/main/docs/how-to-guides/sync-dicom-metadata-to-fhir.md#update-key-vault-for-dicom-cast
-
-9. Restart DICOM Cast container. It should be running successfully.
-
diff --git a/converter/dicom-cast/samples/templates/default-azuredeploy.json b/converter/dicom-cast/samples/templates/default-azuredeploy.json
deleted file mode 100644
index 330e402645..0000000000
--- a/converter/dicom-cast/samples/templates/default-azuredeploy.json
+++ /dev/null
@@ -1,298 +0,0 @@
-{
- "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "parameters": {
- "serviceName": {
- "minLength": 3,
- "maxLength": 24,
- "type": "String",
- "metadata": {
- "description": "Name of the DICOM Cast service container group."
- }
- },
- "image": {
- "defaultValue": "dicomoss.azurecr.io/dicom-cast",
- "type": "String",
- "metadata": {
- "description": "Container image to deploy. Should be of the form repoName/imagename for images stored in public Docker Hub, or a fully qualified URI for other registries. Images from private registries require additional registry credentials."
- }
- },
- "imageTag": {
- "type": "String",
- "metadata": {
- "description": "Image tag. Ex: 10.0.479. You can find the latest https://github.com/microsoft/dicom-server/tags"
- }
- },
- "storageAccountSku": {
- "defaultValue": "Standard_LRS",
- "allowedValues": [
- "Standard_LRS",
- "Standard_GRS",
- "Standard_RAGRS",
- "Standard_ZRS",
- "Premium_LRS",
- "Premium_ZRS",
- "Standard_GZRS",
- "Standard_RAGZRS"
- ],
- "type": "String"
- },
- "deployApplicationInsights": {
- "defaultValue": true,
- "type": "Bool",
- "metadata": {
- "description": "Deploy Application Insights for the DICOM server. Disabled for Microsoft Azure Government (MAG)"
- }
- },
- "applicationInsightsLocation": {
- "defaultValue": "[resourceGroup().location]",
- "allowedValues": [
- "southeastasia",
- "northeurope",
- "westeurope",
- "eastus",
- "southcentralus",
- "westus2"
- ],
- "type": "String"
- },
- "cpuCores": {
- "defaultValue": "1.0",
- "type": "String",
- "metadata": {
- "description": "The number of CPU cores to allocate to the container."
- }
- },
- "memoryInGb": {
- "defaultValue": "1.5",
- "type": "String",
- "metadata": {
- "description": "The amount of memory to allocate to the container in gigabytes."
- }
- },
- "location": {
- "defaultValue": "[resourceGroup().location]",
- "type": "String",
- "metadata": {
- "description": "Location for all resources."
- }
- },
- "restartPolicy": {
- "defaultValue": "always",
- "allowedValues": [
- "never",
- "always",
- "onfailure"
- ],
- "type": "String",
- "metadata": {
- "description": "The behavior of Azure runtime if container has stopped."
- }
- },
- "dicomWebEndpoint": {
- "type": "string",
- "metadata": {
- "description": "The endpoint of the DICOM Web server."
- }
- },
- "fhirEndpoint": {
- "type": "string",
- "metadata": {
- "description": "The endpoint of the FHIR server."
- }
- },
- "patientSystemId": {
- "type": "string",
- "metadata": {
- "description": "Patient SystemId configured by the user"
- }
- },
- "isIssuerIdUsed": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Issuer id or patient system id used based on this boolean value"
- }
- },
- "enforceValidationOfTagValues": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Enforce validation of all tag values and do not store to FHIR even if only non-required tags are invalid"
- }
- },
- "ignoreJsonParsingErrors": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Ignore json parsing errors for DICOM instances with malformed DICOM json"
- }
- },
- "additionalEnvironmentVariables": {
- "defaultValue": [],
- "type": "array",
- "metadata": {
- "description": "Array of additional enviornment variables with objects with properties 'name' and 'value'. ex: [{\"name\": \"testName\", \"value\": \"testValue\"}]"
- }
- }
- },
- "variables": {
- "isMAG": "[or(contains(resourceGroup().location,'usgov'),contains(resourceGroup().location,'usdod'))]",
- "serviceName": "[toLower(parameters('serviceName'))]",
- "containerGroupResourceId": "[resourceId('Microsoft.ContainerInstance/containerGroups/', variables('serviceName'))]",
- "deployAppInsights": "[and(parameters('deployApplicationInsights'),not(variables('isMAG')))]",
- "appInsightsName": "[concat('AppInsights-', variables('serviceName'))]",
- "storageAccountName": "[concat(substring(replace(variables('serviceName'), '-', ''), 0, min(11, length(variables('serviceName')))), uniquestring(resourceGroup().id))]",
- "storageResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
- "keyVaultEndpoint": "[if(variables('isMAG'), concat('https://', variables('serviceName'), '.vault.usgovcloudapi.net/'), concat('https://', variables('serviceName'), '.vault.azure.net/'))]",
- "keyVaultResourceId": "[resourceId('Microsoft.KeyVault/vaults', variables('serviceName'))]",
- "environmentVariables": [
- {
- "name": "Fhir__Endpoint",
- "value": "[parameters('fhirEndpoint')]"
- },
- {
- "name": "DicomWeb__Endpoint",
- "value": "[parameters('dicomWebEndpoint')]"
- },
- {
- "name": "KeyVault__Endpoint",
- "value": "[variables('keyVaultEndpoint')]"
- },
- {
- "name": "DicomCast__Features__EnforceValidationOfTagValues",
- "value": "[parameters('enforceValidationOfTagValues')]"
- },
- {
- "name": "DicomCast__Features__IgnoreJsonParsingErrors",
- "value": "[parameters('ignoreJsonParsingErrors')]"
- },
- {
- "name": "Patient__PatientSystemId",
- "value": "[parameters('patientSystemId')]"
- },
- {
- "name": "Patient__IsIssuerIdUsed",
- "value": "[parameters('isIssuerIdUsed')]"
- }
- ]
- },
- "resources": [
- {
- "type": "Microsoft.ContainerInstance/containerGroups",
- "apiVersion": "2018-10-01",
- "name": "[variables('serviceName')]",
- "location": "[parameters('location')]",
- "dependsOn": [
- "[concat('Microsoft.Insights/components/', variables('appInsightsName'))]"
- ],
- "identity": {
- "type": "SystemAssigned"
- },
- "properties": {
- "containers": [
- {
- "name": "[variables('serviceName')]",
- "properties": {
- "image": "[concat(parameters('image'), ':', parameters('imageTag'))]",
- "resources": {
- "requests": {
- "cpu": "[parameters('cpuCores')]",
- "memoryInGb": "[parameters('memoryInGb')]"
- }
- },
- "environmentVariables": "[concat(variables('environmentVariables'), parameters('additionalEnvironmentVariables'), array(createObject('name', 'ApplicationInsights__InstrumentationKey', 'value', if(variables('deployAppInsights'), reference(concat('Microsoft.Insights/components/', variables('appInsightsName'))).InstrumentationKey, ''))))]"
- }
- }
- ],
- "osType": "Linux",
- "restartPolicy": "[parameters('restartPolicy')]"
- }
- },
- {
- "type": "Microsoft.Insights/components",
- "apiVersion": "2015-05-01",
- "name": "[variables('appInsightsName')]",
- "location": "[parameters('applicationInsightsLocation')]",
- "tags": {
- "[concat('hidden-link:', variables('containerGroupResourceId'))]": "Resource",
- "displayName": "AppInsightsComponent"
- },
- "kind": "web",
- "properties": {
- "Application_Type": "web",
- "ApplicationId": "[variables('serviceName')]"
- },
- "condition": "[variables('deployAppInsights')]"
- },
- {
- "type": "Microsoft.Storage/storageAccounts",
- "apiVersion": "2019-04-01",
- "name": "[variables('storageAccountName')]",
- "location": "[resourceGroup().location]",
- "tags": {},
- "sku": {
- "name": "[parameters('storageAccountSku')]"
- },
- "kind": "StorageV2",
- "properties": {
- "accessTier": "Hot",
- "supportsHttpsTrafficOnly": "true"
- }
- },
- {
- "type": "Microsoft.KeyVault/vaults",
- "apiVersion": "2015-06-01",
- "name": "[variables('serviceName')]",
- "location": "[resourceGroup().location]",
- "dependsOn": [
- "[variables('containerGroupResourceId')]"
- ],
- "tags": {},
- "properties": {
- "sku": {
- "family": "A",
- "name": "Standard"
- },
- "tenantId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]",
- "accessPolicies": [
- {
- "tenantId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]",
- "objectId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.principalId]",
- "permissions": {
- "secrets": [
- "get",
- "list",
- "set"
- ]
- }
- }
- ],
- "enabledForDeployment": false
- }
- },
- {
- "type": "Microsoft.KeyVault/vaults/secrets",
- "apiVersion": "2015-06-01",
- "name": "[concat(variables('serviceName'), '/TableStore--ConnectionString')]",
- "dependsOn": [
- "[variables('keyVaultResourceId')]",
- "[variables('storageResourceId')]"
- ],
- "properties": {
- "contentType": "text/plain",
- "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageResourceId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value, ';')]"
- }
- }
- ],
- "outputs": {
- "containerTenantId": {
- "type": "string",
- "value": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]"
- },
- "containerPrincipalId": {
- "type": "string",
- "value": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.principalId]"
- }
- }
-}
diff --git a/converter/dicom-cast/samples/templates/deploy-with-healthcareapis.json b/converter/dicom-cast/samples/templates/deploy-with-healthcareapis.json
deleted file mode 100644
index 0686c9349c..0000000000
--- a/converter/dicom-cast/samples/templates/deploy-with-healthcareapis.json
+++ /dev/null
@@ -1,319 +0,0 @@
-{
- "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
- "contentVersion": "1.0.0.0",
- "parameters": {
- "workspaceRegion": {
- "defaultValue": "[resourceGroup().location",
- "type": "string",
- "allowedValues": [
- "australiaeast",
- "canadacentral",
- "eastus",
- "eastus2",
- "germanywestcentral",
- "japaneast",
- "northcentralus",
- "northeurope",
- "southafricanorth",
- "southcentralus",
- "southeastasia",
- "switzerlandnorth",
- "uksouth",
- "ukwest",
- "westcentralus",
- "westeurope",
- "westus2"
- ]
- },
- "workspaceName": {
- "type": "string",
- "metadata": {
- "description": "Name of the workspace."
- }
- },
- "fhirServiceName": {
- "type": "string",
- "metadata": {
- "description": "Name of the workspace FHIR service."
- }
- },
- "dicomServiceName": {
- "type": "string",
- "metadata": {
- "description": "Name of the workspace DICOM service."
- }
- },
- "serviceName": {
- "minLength": 3,
- "maxLength": 24,
- "type": "string",
- "metadata": {
- "description": "Name of the DICOM Cast service container group."
- }
- },
- "image": {
- "defaultValue": "dicomoss.azurecr.io/dicom-cast",
- "type": "string",
- "metadata": {
- "description": "Container image to deploy. Should be of the form repoName/imagename for images stored in public Docker Hub, or a fully qualified URI for other registries. Images from private registries require additional registry credentials."
- }
- },
- "imageTag": {
- "type": "string",
- "metadata": {
- "description": "Image tag. Ex: 10.0.479. You can find the latest https://github.com/microsoft/dicom-server/tags"
- }
- },
- "storageAccountSku": {
- "defaultValue": "Standard_LRS",
- "allowedValues": [
- "Standard_LRS",
- "Standard_GRS",
- "Standard_RAGRS",
- "Standard_ZRS",
- "Premium_LRS",
- "Premium_ZRS",
- "Standard_GZRS",
- "Standard_RAGZRS"
- ],
- "type": "string"
- },
- "deployApplicationInsights": {
- "defaultValue": true,
- "type": "bool",
- "metadata": {
- "description": "Deploy Application Insights for the DICOM server. Disabled for Microsoft Azure Government (MAG)"
- }
- },
- "applicationInsightsLocation": {
- "defaultValue": "[resourceGroup().location]",
- "allowedValues": [
- "southeastasia",
- "northeurope",
- "westeurope",
- "eastus",
- "southcentralus",
- "westus2"
- ],
- "type": "string"
- },
- "cpuCores": {
- "defaultValue": "1.0",
- "type": "string",
- "metadata": {
- "description": "The number of CPU cores to allocate to the container."
- }
- },
- "memoryInGb": {
- "defaultValue": "1.5",
- "type": "string",
- "metadata": {
- "description": "The amount of memory to allocate to the container in gigabytes."
- }
- },
- "location": {
- "defaultValue": "[resourceGroup().location]",
- "type": "string",
- "metadata": {
- "description": "Location for all resources."
- }
- },
- "restartPolicy": {
- "defaultValue": "always",
- "allowedValues": [
- "never",
- "always",
- "onfailure"
- ],
- "type": "string",
- "metadata": {
- "description": "The behavior of Azure runtime if container has stopped."
- }
- },
- "patientSystemId": {
- "defaultValue": "",
- "type": "string",
- "metadata": {
- "description": "Patient SystemId configured by the user"
- }
- },
- "isIssuerIdUsed": {
- "defaultValue": false,
- "type": "bool",
- "metadata": {
- "description": "Issuer id or patient system id used based on this boolean value"
- }
- },
- "enforceValidationOfTagValues": {
- "defaultValue": false,
- "type": "bool",
- "metadata": {
- "description": "Enforce validation of all tag values and do not store to FHIR even if only non-required tags are invalid"
- }
- },
- "ignoreJsonParsingErrors": {
- "defaultValue": false,
- "type": "Bool",
- "metadata": {
- "description": "Ignore json parsing errors for DICOM instances with malformed DICOM json"
- }
- }
- },
- "variables": {
- "authority": "[concat('https://login.microsoftonline.com/', subscription().tenantId)]",
- "fhirServiceName": "[concat(parameters('workspaceName'), '/', parameters('fhirServiceName'))]",
- "dicomServiceName": "[concat(parameters('workspaceName'), '/', parameters('dicomServiceName'))]",
- "fhirEndpoint": "[concat('https://', parameters('workspaceName'), '-', parameters('fhirServiceName'), '.fhir.azurehealthcareapis.com')]",
- "dicomWebEndpoint": "[concat('https://', parameters('workspaceName'), '-', parameters('dicomServiceName'), '.dicom.azurehealthcareapis.com')]",
- "authenticationArray": [
- {
- "name": "DicomWeb__Authentication__AuthenticationType",
- "value": "ManagedIdentity"
- },
- {
- "name": "DicomWeb__Authentication__Enabled",
- "value": "true"
- },
- {
- "name": "DicomWeb__Authentication__ManagedIdentityCredential__Resource",
- "value": "https://dicom.healthcareapis.azure.com"
- },
- {
- "name": "Fhir__Authentication__AuthenticationType",
- "value": "ManagedIdentity"
- },
- {
- "name": "Fhir__Authentication__Enabled",
- "value": "true"
- },
- {
- "name": "Fhir__Authentication__ManagedIdentityCredential__Resource",
- "value": "[variables('fhirEndpoint')]"
- }
- ]
- },
- "resources": [{
- "type": "Microsoft.HealthcareApis/workspaces",
- "name": "[parameters('workspaceName')]",
- "apiVersion": "2021-11-01",
- "location": "[parameters('workspaceRegion')]",
- "properties": {}
- },
- {
- "type": "Microsoft.HealthcareApis/workspaces/fhirservices",
- "kind": "fhir-R4",
- "name": "[variables('fhirServiceName')]",
- "apiVersion": "2021-11-01",
- "location": "[parameters('workspaceRegion')]",
- "dependsOn": [
- "[resourceId('Microsoft.HealthcareApis/workspaces', parameters('workspaceName'))]"
- ],
- "properties": {
- "authenticationConfiguration": {
- "authority": "[variables('authority')]",
- "audience": "[variables('fhirEndpoint')]"
- }
- }
- },
- {
- "type": "Microsoft.HealthcareApis/workspaces/dicomservices",
- "name": "[variables('dicomServiceName')]",
- "apiVersion": "2021-11-01",
- "location": "[parameters('workspaceRegion')]",
- "dependsOn": [
- "[resourceId('Microsoft.HealthcareApis/workspaces', parameters('workspaceName'))]"
- ],
- "properties": {}
- },
- {
- "type": "Microsoft.Resources/deployments",
- "apiVersion": "2021-04-01",
- "name": "linkedTemplate",
- "dependsOn": [
- "[resourceId('Microsoft.HealthcareApis/workspaces/fhirservices', parameters('workspaceName'), parameters('fhirServiceName'))]",
- "[resourceId('Microsoft.HealthcareApis/workspaces/dicomservices', parameters('workspaceName'), parameters('dicomServiceName'))]"
- ],
- "properties": {
- "mode": "Incremental",
- "templateLink": {
- "uri": "https://raw.githubusercontent.com/microsoft/dicom-server/main/converter/dicom-cast/samples/templates/default-azuredeploy.json",
- "contentVersion": "1.0.0.0"
- },
- "parameters": {
- "serviceName": {
- "value": "[parameters('serviceName')]"
- },
- "imageTag": {
- "value": "[parameters('imageTag')]"
- },
- "image": {
- "value": "[parameters('image')]"
- },
- "storageAccountSku": {
- "value": "[parameters('storageAccountSku')]"
- },
- "deployApplicationInsights": {
- "value": "[parameters('deployApplicationInsights')]"
- },
- "applicationInsightsLocation": {
- "value": "[parameters('applicationInsightsLocation')]"
- },
- "cpuCores": {
- "value": "[parameters('cpuCores')]"
- },
- "memoryInGb": {
- "value": "[parameters('memoryInGb')]"
- },
- "location": {
- "value": "[parameters('location')]"
- },
- "dicomWebEndpoint": {
- "value": "[variables('dicomWebEndpoint')]"
- },
- "fhirEndpoint": {
- "value": "[variables('fhirEndpoint')]"
- },
- "enforceValidationOfTagValues": {
- "value": "[parameters('enforceValidationOfTagValues')]"
- },
- "ignoreJsonParsingErrors": {
- "value": "[parameters('ignoreJsonParsingErrors')]"
- },
- "additionalEnvironmentVariables": {
- "value": "[variables('authenticationArray')]"
- },
- "patientSystemId": {
- "value": "[parameters('patientSystemId')]"
- },
- "isIssuerIdUsed": {
- "value": "[parameters('isIssuerIdUsed')]"
- }
- }
- }
- },
- {
- "type": "Microsoft.HealthcareApis/workspaces/providers/roleAssignments",
- "apiVersion": "2021-04-01-preview",
- "name": "[concat(parameters('workspaceName'), '/Microsoft.Authorization/', guid(parameters('fhirServiceName')))]",
- "dependsOn": [
- "[resourceId('Microsoft.Resources/deployments', 'linkedTemplate')]"
- ],
- "properties": {
- "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions/', '5a1fc7df-4bf1-4951-a576-89034ee01acd')]",
- "principalId": "[reference('linkedTemplate').outputs.containerPrincipalId.value]"
- }
- },
- {
- "type": "Microsoft.HealthcareApis/workspaces/providers/roleAssignments",
- "apiVersion": "2021-04-01-preview",
- "name": "[concat(parameters('workspaceName'), '/Microsoft.Authorization/', guid(parameters('dicomServiceName')))]",
- "dependsOn": [
- "[resourceId('Microsoft.Resources/deployments', 'linkedTemplate')]"
- ],
- "properties": {
- "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions/', '58a3b984-7adf-4c20-983a-32417c86fbc8')]",
- "principalId": "[reference('linkedTemplate').outputs.containerPrincipalId.value]"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/ChangeFeedGenerator.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/ChangeFeedGenerator.cs
deleted file mode 100644
index aea8f6c74f..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/ChangeFeedGenerator.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using FellowOakDicom;
-using Microsoft.Health.Dicom.Client.Models;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests;
-
-public static class ChangeFeedGenerator
-{
- public static ChangeFeedEntry Generate(long? sequence = null, ChangeFeedAction? action = null, string studyInstanceUid = null, string seriesInstanceUid = null, string sopInstanceUid = null, ChangeFeedState? state = null, DicomDataset metadata = null)
- {
- if (sequence == null)
- {
- sequence = 1;
- }
-
- if (action == null)
- {
- action = ChangeFeedAction.Create;
- }
-
- if (string.IsNullOrEmpty(studyInstanceUid))
- {
- studyInstanceUid = DicomUID.Generate().UID;
- }
-
- if (string.IsNullOrEmpty(seriesInstanceUid))
- {
- seriesInstanceUid = DicomUID.Generate().UID;
- }
-
- if (string.IsNullOrEmpty(sopInstanceUid))
- {
- sopInstanceUid = DicomUID.Generate().UID;
- }
-
- if (state == null)
- {
- state = ChangeFeedState.Current;
- }
-
- return new ChangeFeedEntry(
- sequence.Value,
- DateTimeOffset.UtcNow,
- action.Value,
- studyInstanceUid,
- seriesInstanceUid,
- sopInstanceUid,
- state.Value,
- metadata: metadata);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirResourceValidatorTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirResourceValidatorTests.cs
deleted file mode 100644
index 610121edfa..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirResourceValidatorTests.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Xunit;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Fhir;
-
-public class FhirResourceValidatorTests
-{
- private readonly FhirResourceValidator _fhirResourceValidator = new FhirResourceValidator();
-
- [Fact]
- public void GivenAResourceMissingId_WhenValidated_ThenInvalidFhirResourceExceptionShouldBeThrown()
- {
- var patient = new Patient();
-
- Assert.Throws(() => _fhirResourceValidator.Validate(patient));
- }
-
- [Fact]
- public void GivenAResourceMissingMeta_WhenValidated_ThenInvalidFhirResourceExceptionShouldBeThrown()
- {
- var patient = new Patient()
- {
- Id = "p1",
- Meta = null,
- };
-
- Assert.Throws(() => _fhirResourceValidator.Validate(patient));
- }
-
- [Fact]
- public void GivenAResourceMissingVersionId_WhenValidated_ThenInvalidFhirResourceExceptionShouldBeThrown()
- {
- var patient = new Patient()
- {
- Id = "p1",
- Meta = new Meta(),
- };
-
- Assert.Throws(() => _fhirResourceValidator.Validate(patient));
- }
-
- [Fact]
- public void GivenAValidResource_WhenValidated_ThenItShouldNotThrowException()
- {
- var patient = new Patient()
- {
- Id = "p1",
- Meta = new Meta()
- {
- VersionId = "1",
- },
- };
-
- _fhirResourceValidator.Validate(patient);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirServiceTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirServiceTests.cs
deleted file mode 100644
index 219b579f06..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirServiceTests.cs
+++ /dev/null
@@ -1,277 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Extensions;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.Fhir.Client;
-using NSubstitute;
-using Xunit;
-using static Hl7.Fhir.Model.CapabilityStatement;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Fhir;
-
-public class FhirServiceTests
-{
- private static readonly CancellationToken DefaultCancellationToken = new CancellationTokenSource().Token;
- private static readonly Identifier DefaultPatientIdentifier = new Identifier(string.Empty, "p1");
- private static readonly Identifier DefaultImagingStudyIdentifier = new Identifier(string.Empty, "123");
- private const string MetaDataEndpoint = "metadata";
-
- private readonly IFhirClient _fhirClient = Substitute.For();
- private readonly IFhirResourceValidator _fhirResourceValidator = Substitute.For();
- private readonly FhirService _fhirService;
-
- public FhirServiceTests()
- {
- _fhirService = new FhirService(_fhirClient, _fhirResourceValidator);
- }
-
- private delegate Task RetrieveAsyncDelegate(Identifier identifier, CancellationToken cancellationToken);
-
- [Fact]
- public async Task GivenNoMatchingResource_WhenPatientIsRetrieved_ThenItShouldNotBeValidated()
- {
- await ExecuteAndValidateNoMatch(DefaultPatientIdentifier, _fhirService.RetrievePatientAsync);
- }
-
- [Fact]
- public async Task GivenASingleMatch_WhenPatientIsRetrieved_ThenItShouldBeValidated()
- {
- await ExecuteAndValidateSingleMatchAsync(DefaultPatientIdentifier, _fhirService.RetrievePatientAsync);
- }
-
- [Fact]
- public async Task GivenASingleMatchNotInFirstResultSet_WhenPatientIsRetrieved_ThenCorrectPatientShouldBeReturned()
- {
- await ExecuteAndValidateSingleMatchNotInFirstResultSetAsync(DefaultPatientIdentifier, _fhirService.RetrievePatientAsync);
- }
-
- [Fact]
- public async Task GivenMultipleMatches_WhenPatientIsRetrieved_ThenMultipleMatchingResourcesExceptionShouldBeThrown()
- {
- await ExecuteAndValidateMultipleMatchesAsync(_fhirService.RetrievePatientAsync);
- }
-
- [Fact]
- public async Task GivenMultipleMatchesThatSpansMultipleResultSet_WhenPatientIsRetrieved_ThenMultipleMatchingResourcesExceptionShouldBeThrown()
- {
- await ExecuteAndValidateMultipleMatchesThatSpansInMultipleResultSetAsync(DefaultImagingStudyIdentifier, _fhirService.RetrievePatientAsync);
- }
-
- [Fact]
- public async Task GivenNoMatchingResource_WhenImagingStudyIsRetrieved_ThenItShouldNotBeValidated()
- {
- await ExecuteAndValidateNoMatch(DefaultPatientIdentifier, _fhirService.RetrieveImagingStudyAsync);
- }
-
- [Fact]
- public async Task GivenASingleMatch_WhenImagingStudyIsRetrieved_ThenItShouldBeValidated()
- {
- await ExecuteAndValidateSingleMatchAsync(DefaultImagingStudyIdentifier, _fhirService.RetrieveImagingStudyAsync);
- }
-
- [Fact]
- public async Task GivenASingleMatchNotInFirstResultSet_WhenImagingStudyIsRetrieved_ThenCorrectImagingStudyShouldBeReturned()
- {
- await ExecuteAndValidateSingleMatchNotInFirstResultSetAsync(DefaultImagingStudyIdentifier, _fhirService.RetrieveImagingStudyAsync);
- }
-
- [Fact]
- public async Task GivenMultipleMatches_WhenImagingStudyIsRetrieved_ThenMultipleMatchingResourcesExceptionShouldBeThrown()
- {
- await ExecuteAndValidateMultipleMatchesAsync(_fhirService.RetrieveImagingStudyAsync);
- }
-
- [Fact]
- public async Task GivenMultipleMatchesThatSpansMultipleResultSet_WhenImagingStudyIsRetrieved_ThenMultipleMatchingResourcesExceptionShouldBeThrown()
- {
- await ExecuteAndValidateMultipleMatchesThatSpansInMultipleResultSetAsync(DefaultImagingStudyIdentifier, _fhirService.RetrieveImagingStudyAsync);
- }
-
- [Fact]
- public async Task GivenInValidFhirConfigVersion_ShouldThrowError()
- {
- _fhirClient.ReadAsync(MetaDataEndpoint, DefaultCancellationToken).Returns(GenerateFhirCapabilityResponse(FHIRVersion.N0_01, SystemRestfulInteraction.Transaction));
- await Assert.ThrowsAsync(() => _fhirService.CheckFhirServiceCapability(DefaultCancellationToken));
- }
-
- [Fact]
- public async Task GivenInValidFhirConfigInteraction_ShouldThrowError()
- {
- _fhirClient.ReadAsync(MetaDataEndpoint, DefaultCancellationToken).Returns(GenerateFhirCapabilityResponse(FHIRVersion.N4_0_0, SystemRestfulInteraction.Batch));
- await Assert.ThrowsAsync(() => _fhirService.CheckFhirServiceCapability(DefaultCancellationToken));
- }
-
- [Fact]
- public async Task GivenValidFhirConfigV4_ShouldNotThrowError()
- {
- _fhirClient.ReadAsync(MetaDataEndpoint, DefaultCancellationToken).Returns(GenerateFhirCapabilityResponse(FHIRVersion.N4_0_0, SystemRestfulInteraction.Transaction));
- await _fhirService.CheckFhirServiceCapability(DefaultCancellationToken);
- }
-
- [Fact]
- public async Task GivenValidFhirConfigV401_ShouldNotThrowError()
- {
- _fhirClient.ReadAsync(MetaDataEndpoint, DefaultCancellationToken).Returns(GenerateFhirCapabilityResponse(FHIRVersion.N4_0_1, SystemRestfulInteraction.Transaction));
- await _fhirService.CheckFhirServiceCapability(DefaultCancellationToken);
- }
-
- private void SetupIdentifierSearchCriteria(string typeName, Identifier identifier, Bundle bundle)
- {
- ResourceType? resourceType = ModelInfo.FhirTypeNameToResourceType(typeName);
- Assert.NotNull(resourceType);
-
- _fhirClient.SearchAsync(
- resourceType.Value,
- identifier.ToSearchQueryParameter(),
- count: null,
- DefaultCancellationToken)
- .Returns(GenerateFhirResponse(bundle));
- }
-
- private async Task ExecuteAndValidateNoMatch(Identifier identifier, RetrieveAsyncDelegate retrieve)
- where TResource : Resource, new()
- {
- SetupIdentifierSearchCriteria(new TResource().TypeName, identifier, new Bundle());
-
- TResource resource = await retrieve(identifier, DefaultCancellationToken);
-
- Assert.Null(resource);
- _fhirResourceValidator.DidNotReceiveWithAnyArgs().Validate(default);
- }
-
- private async Task ExecuteAndValidateSingleMatchAsync(Identifier identifier, RetrieveAsyncDelegate retrieve)
- where TResource : Resource, new()
- {
- var expectedResource = new TResource();
-
- var bundle = new Bundle();
-
- bundle.Entry.Add(
- new Bundle.EntryComponent()
- {
- Resource = expectedResource,
- });
-
- SetupIdentifierSearchCriteria(expectedResource.TypeName, identifier, bundle);
-
- TResource actualResource = await retrieve(identifier, DefaultCancellationToken);
-
- Assert.Same(expectedResource, actualResource);
- _fhirResourceValidator.Received(1).Validate(expectedResource);
- }
-
- private async Task ExecuteAndValidateSingleMatchNotInFirstResultSetAsync(Identifier identifier, RetrieveAsyncDelegate retrieve)
- where TResource : Resource, new()
- {
- var expectedResource = new TResource();
- Assert.True(expectedResource.TryDeriveResourceType(out ResourceType resourceType));
-
- var firstBundle = new Bundle()
- {
- NextLink = new Uri("next", UriKind.Relative),
- };
-
- _fhirClient.SearchAsync(
- resourceType,
- query: Arg.Any(),
- count: Arg.Any(),
- cancellationToken: DefaultCancellationToken)
- .Returns(GenerateFhirResponse(firstBundle));
-
- var bundle = new Bundle();
-
- bundle.Entry.Add(
- new Bundle.EntryComponent()
- {
- Resource = expectedResource,
- });
-
- _fhirClient.SearchAsync(url: Arg.Any(), DefaultCancellationToken).Returns(GenerateFhirResponse(bundle));
-
- TResource actualResource = await retrieve(identifier, DefaultCancellationToken);
-
- Assert.Same(expectedResource, actualResource);
- }
-
- private async Task ExecuteAndValidateMultipleMatchesAsync(RetrieveAsyncDelegate retrieve)
- where TResource : Resource, new()
- {
- var expectedResource = new TResource();
- Assert.True(expectedResource.TryDeriveResourceType(out ResourceType resourceType));
-
- var bundleEntry = new Bundle.EntryComponent()
- {
- Resource = expectedResource,
- };
-
- var bundle = new Bundle();
-
- bundle.Entry.AddRange(new[] { bundleEntry, bundleEntry });
-
- _fhirClient.SearchAsync(
- resourceType,
- query: Arg.Any(),
- count: Arg.Any(),
- cancellationToken: DefaultCancellationToken)
- .Returns(GenerateFhirResponse(bundle));
-
- await Assert.ThrowsAsync(() => retrieve(new Identifier(), DefaultCancellationToken));
- }
-
- private async Task ExecuteAndValidateMultipleMatchesThatSpansInMultipleResultSetAsync(Identifier identifier, RetrieveAsyncDelegate retrieve)
- where TResource : Resource, new()
- {
- var expectedResource = new TResource();
-
- var firstBundle = new Bundle()
- {
- NextLink = new Uri("next", UriKind.Relative),
- };
-
- var bundleEntry = new Bundle.EntryComponent()
- {
- Resource = expectedResource,
- };
-
- firstBundle.Entry.Add(bundleEntry);
-
- SetupIdentifierSearchCriteria(expectedResource.TypeName, identifier, firstBundle);
-
- var secondBundle = new Bundle();
-
- secondBundle.Entry.Add(bundleEntry);
-
- _fhirClient.SearchAsync(url: Arg.Any(), DefaultCancellationToken).Returns(GenerateFhirResponse(secondBundle));
-
- await Assert.ThrowsAsync(() => retrieve(identifier, DefaultCancellationToken));
- }
-
- private static FhirResponse GenerateFhirResponse(Bundle firstBundle)
- {
- return new FhirResponse(new HttpResponseMessage(), firstBundle);
- }
-
- private static FhirResponse GenerateFhirCapabilityResponse(FHIRVersion version, SystemRestfulInteraction interaction)
- {
- var statement = new CapabilityStatement { FhirVersion = version };
- statement.Rest.Add(new RestComponent
- {
- Interaction = new List
- {
- new SystemInteractionComponent { Code = interaction },
- },
- });
-
- return new FhirResponse(new HttpResponseMessage(), statement);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirTransactionExecutorTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirTransactionExecutorTests.cs
deleted file mode 100644
index 63b99a42c7..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/FhirTransactionExecutorTests.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Net;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Hl7.Fhir.Model;
-using Hl7.Fhir.Utility;
-using Microsoft.Health.DicomCast.Core.Exceptions;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.Fhir.Client;
-using NSubstitute;
-using NSubstitute.ExceptionExtensions;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Fhir;
-
-public class FhirTransactionExecutorTests
-{
- private readonly IFhirClient _fhirClient = Substitute.For();
- private readonly FhirTransactionExecutor _fhirTransactionExecutor;
-
- private readonly Bundle _defaultRequestBundle = new Bundle();
-
- public FhirTransactionExecutorTests()
- {
- _fhirTransactionExecutor = new FhirTransactionExecutor(_fhirClient);
- }
-
- [Fact]
- public async Task GivenRequestFailsWithPreconditionFailed_WhenExecuting_ThenResourceConflictExceptionShouldBeThrown()
- {
- SetupPostException(HttpStatusCode.PreconditionFailed);
-
- await Assert.ThrowsAsync(() => _fhirTransactionExecutor.ExecuteTransactionAsync(new Bundle(), default));
- }
-
- [Fact]
- public async Task GivenRequestFailsWithTooManyRequests_WhenExecuting_ThenServerTooBusyExceptionShouldBeThrown()
- {
- SetupPostException(HttpStatusCode.TooManyRequests);
-
- await Assert.ThrowsAsync(() => _fhirTransactionExecutor.ExecuteTransactionAsync(new Bundle(), default));
- }
-
- [Fact]
- public async Task GivenRequestFailsWithAnyOtherReason_WhenExecuting_ThenTransactionFailedExceptionShouldBeThrown()
- {
- var expectedOperationOutcome = new OperationOutcome();
- SetupPostException(HttpStatusCode.NotFound, expectedOperationOutcome);
-
- TransactionFailedException exception = await Assert.ThrowsAsync(
- () => _fhirTransactionExecutor.ExecuteTransactionAsync(new Bundle(), default));
-
- Assert.Same(expectedOperationOutcome, exception.OperationOutcome);
- }
-
- [Fact]
- public async Task GivenNullResponse_WhenTransactionIsExecuted_ThenInvalidFhirResponseExceptionShouldBeThrown()
- {
- var bundle = new Bundle();
-
- _fhirClient.PostBundleAsync(bundle).Returns(new FhirResponse(new HttpResponseMessage(), null));
-
- await Assert.ThrowsAsync(() => _fhirTransactionExecutor.ExecuteTransactionAsync(bundle, default));
- }
-
- [Fact]
- public async Task GivenResponseBundleEntryCountDoesNotMatch_WhenTransactionIsExecuted_ThenInvalidFhirResponseExceptionShouldBeThrown()
- {
- // Request will have 1 entry but response will have 0.
- AddEntryComponentToDefaultRequestBundle();
-
- _fhirClient.PostBundleAsync(_defaultRequestBundle).Returns(new FhirResponse(new HttpResponseMessage(HttpStatusCode.OK), new Bundle()));
-
- await Assert.ThrowsAsync(() => ExecuteTransactionAsync());
- }
-
- [Fact]
- public async Task GivenRequestsIsSuccessful_WhenTransactionIsExecuted_ThenCorrectBundleShouldBeReturned()
- {
- Bundle expectedBundle = SetupTransaction("200");
-
- Bundle actualBundle = await ExecuteTransactionAsync();
-
- Assert.Same(expectedBundle, actualBundle);
-
- // The annotation should be set.
- Assert.True(actualBundle.Entry[0].Response.TryGetAnnotation(typeof(HttpStatusCode), out object statusCode));
-
- Assert.Equal(HttpStatusCode.OK, (HttpStatusCode)statusCode);
- }
-
- [Fact]
- public async Task GivenNullEntryComponent_WhenTransactionIsExecuted_ThenInvalidFhirResponseExceptionShouldBeThrown()
- {
- var bundle = new Bundle();
-
- bundle.Entry.Add(null);
- _fhirClient.PostBundleAsync(_defaultRequestBundle).Returns(new FhirResponse(new HttpResponseMessage(HttpStatusCode.OK), bundle));
-
- await Assert.ThrowsAsync(() => ExecuteTransactionAsync());
- }
-
- [Fact]
- public async Task GivenNullEntryComponentResponse_WhenTransactionIsExecuted_ThenInvalidFhirResponseExceptionShouldBeThrown()
- {
- var bundle = new Bundle();
-
- bundle.Entry.Add(new Bundle.EntryComponent());
- _fhirClient.PostBundleAsync(_defaultRequestBundle).Returns(new FhirResponse(new HttpResponseMessage(HttpStatusCode.OK), bundle));
-
- await Assert.ThrowsAsync(() => ExecuteTransactionAsync());
- }
-
- [Fact]
- public async Task GivenNullEntryComponentResponseStatus_WhenTransactionIsExecuted_ThenInvalidFhirResponseExceptionShouldBeThrown()
- {
- var bundle = new Bundle();
-
- bundle.Entry.Add(new Bundle.EntryComponent()
- {
- Response = new Bundle.ResponseComponent(),
- });
- _fhirClient.PostBundleAsync(_defaultRequestBundle).Returns(new FhirResponse(new HttpResponseMessage(HttpStatusCode.OK), bundle));
-
- await Assert.ThrowsAsync(() => ExecuteTransactionAsync());
- }
-
- [Theory]
- [InlineData("")]
- [InlineData("10")]
- [InlineData("1000")]
- [InlineData("10a")]
- [InlineData("299")]
- public async Task GivenInvalidEntryComponentResponseStatusValue_WhenTransactionIsExecuted_ThenInvalidFhirResponseExceptionShouldBeThrown(string invalidStatus)
- {
- var bundle = new Bundle();
-
- bundle.Entry.Add(new Bundle.EntryComponent()
- {
- Response = new Bundle.ResponseComponent()
- {
- Status = invalidStatus,
- },
- });
- _fhirClient.PostBundleAsync(_defaultRequestBundle).Returns(new FhirResponse(new HttpResponseMessage(HttpStatusCode.OK), bundle));
-
- await Assert.ThrowsAsync(() => ExecuteTransactionAsync());
- }
-
- private Bundle SetupTransaction(params string[] statusList)
- {
- for (int i = 0; i < statusList.Length; i++)
- {
- AddEntryComponentToDefaultRequestBundle();
- }
-
- Bundle bundle = GenerateBundleWithStatus(statusList);
-
- _fhirClient.PostBundleAsync(Arg.Any()).Returns(new FhirResponse(new HttpResponseMessage(HttpStatusCode.OK), bundle));
-
- return bundle;
- }
-
- private void AddEntryComponentToDefaultRequestBundle()
- {
- _defaultRequestBundle.Entry.Add(new Bundle.EntryComponent());
- }
-
- private Task ExecuteTransactionAsync()
- => _fhirTransactionExecutor.ExecuteTransactionAsync(_defaultRequestBundle, default);
-
- private static Bundle GenerateBundleWithStatus(params string[] statusList)
- {
- var bundle = new Bundle();
-
- foreach (string status in statusList)
- {
- bundle.Entry.Add(new Bundle.EntryComponent()
- {
- Response = new Bundle.ResponseComponent()
- {
- Status = status,
- },
- });
- }
-
- return bundle;
- }
-
- private void SetupPostException(HttpStatusCode httpStatusCode, OperationOutcome operationOutcome = null)
- {
- if (operationOutcome == null)
- {
- operationOutcome = new OperationOutcome();
- }
-
- var response = new FhirResponse(new HttpResponseMessage(httpStatusCode), operationOutcome);
- _fhirClient.PostBundleAsync(default).ThrowsForAnyArgs(new FhirClientException(response, httpStatusCode));
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/ImagingStudyIdentifierUtilityTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/ImagingStudyIdentifierUtilityTests.cs
deleted file mode 100644
index 21b3a274a2..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Fhir/ImagingStudyIdentifierUtilityTests.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Xunit;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Fhir;
-
-public class ImagingStudyIdentifierUtilityTests
-{
- [Fact]
- public void GivenStudyInstanceUid_WhenCreated_ThenCorrectIdentifierShouldBeCreated()
- {
- var identifier = IdentifierUtility.CreateIdentifier("123");
-
- Assert.NotNull(identifier);
- Assert.Equal("urn:dicom:uid", identifier.System);
- Assert.Equal("urn:oid:123", identifier.Value);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/ChangeFeedProcessorTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/ChangeFeedProcessorTests.cs
deleted file mode 100644
index 6b772db2bc..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/ChangeFeedProcessorTests.cs
+++ /dev/null
@@ -1,507 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Diagnostics;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using FellowOakDicom;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Time.Testing;
-using Microsoft.Health.Dicom.Client.Models;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Exceptions;
-using Microsoft.Health.DicomCast.Core.Features.DicomWeb.Service;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.State;
-using Microsoft.Health.DicomCast.Core.Features.Worker;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using NSubstitute.ExceptionExtensions;
-using Polly.Timeout;
-using Xunit;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker;
-
-public class ChangeFeedProcessorTests
-{
- private static readonly CancellationToken DefaultCancellationToken = new CancellationTokenSource().Token;
-
- private readonly IChangeFeedRetrieveService _changeFeedRetrieveService = Substitute.For();
- private readonly IFhirTransactionPipeline _fhirTransactionPipeline = Substitute.For();
- private readonly ISyncStateService _syncStateService = Substitute.For();
- private readonly IExceptionStore _exceptionStore = Substitute.For();
- private readonly FakeTimeProvider _timeProvider = new FakeTimeProvider(DateTimeOffset.UtcNow);
- private readonly IOptions _config = Substitute.For>();
- private readonly DicomCastConfiguration _dicomCastConfiguration = new DicomCastConfiguration();
- private readonly ChangeFeedProcessor _changeFeedProcessor;
-
- public ChangeFeedProcessorTests()
- {
- _dicomCastConfiguration.Features.IgnoreJsonParsingErrors = true;
- _config.Value.Returns(_dicomCastConfiguration);
-
- _changeFeedProcessor = new ChangeFeedProcessor(
- _changeFeedRetrieveService,
- _fhirTransactionPipeline,
- _syncStateService,
- _exceptionStore,
- _timeProvider,
- _config,
- NullLogger.Instance);
-
- SetupSyncState();
- }
-
- [Fact]
- public async Task GivenMultipleChangeFeedEntries_WhenProcessed_ThenEachChangeFeedEntryShouldBeProcessed()
- {
- const long Latest = 3L;
- ChangeFeedEntry[] changeFeeds = new[]
- {
- ChangeFeedGenerator.Generate(1),
- ChangeFeedGenerator.Generate(2),
- ChangeFeedGenerator.Generate(Latest),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(Latest);
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(2).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(2).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.ReceivedWithAnyArgs(3).ProcessAsync(default, default);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds[0], DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds[1], DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds[2], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task GivenMalformedChangeFeedEntries_WhenProcessed_BadEntryShouldBeSkipped()
- {
- const long Latest = 3L;
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
- ChangeFeedEntry[] changeFeeds2 = new[]
- {
- ChangeFeedGenerator.Generate(Latest),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(Latest);
-
- // call to retrieve batch has json exception
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).ThrowsAsync(new JsonException());
-
- // get the items individually from the change feed
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, 1, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, 1, DefaultCancellationToken).ThrowsAsync(new JsonException());
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(2, 1, DefaultCancellationToken).Returns(changeFeeds2);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(Latest, 1, DefaultCancellationToken).Returns(Array.Empty());
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(2).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(6).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, 1, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(1, 1, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(2, 1, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(Latest, 1, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.ReceivedWithAnyArgs(2).ProcessAsync(default, default);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds2[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task GivenMalformedChangeFeedEntries_WhenProcessedAndNotIgnoringErrors_ExceptionIsThrown()
- {
- const long Latest = 3L;
- _dicomCastConfiguration.Features.IgnoreJsonParsingErrors = false;
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(Latest);
-
- // call to retrieve batch has json exception
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).ThrowsAsync(new JsonException());
-
- // Act
- await Assert.ThrowsAsync(() => ExecuteProcessAsync());
- }
-
- [Fact]
- public async Task GivenMultipleChangeFeedEntries_WhenProcessing_ThenItShouldProcessAllPendinChangeFeedEntries()
- {
- const long Latest = 2L;
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- ChangeFeedEntry[] changeFeeds2 = new[]
- {
- ChangeFeedGenerator.Generate(Latest),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(Latest);
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds2);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(3).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(3).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.ReceivedWithAnyArgs(2).ProcessAsync(default, default);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds2[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task GivenSkippedSequenceNumbers_WhenProcessing_ThenSkipAheadByLimit()
- {
- const long PageOneEnd = 5L;
- const long Latest = PageOneEnd + ChangeFeedProcessor.DefaultLimit + 1; // Fall onto the next page
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- ChangeFeedGenerator.Generate(PageOneEnd),
- };
-
- ChangeFeedEntry[] changeFeeds3 = new[]
- {
- ChangeFeedGenerator.Generate(Latest),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(PageOneEnd, Latest, Latest, Latest);
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(PageOneEnd, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(PageOneEnd + ChangeFeedProcessor.DefaultLimit, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds3);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(4).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(4).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.RetrieveChangeFeedAsync(PageOneEnd, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.RetrieveChangeFeedAsync(PageOneEnd + ChangeFeedProcessor.DefaultLimit, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.RetrieveChangeFeedAsync(Latest, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.ReceivedWithAnyArgs(3).ProcessAsync(default, default);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[1], DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds3[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task WhenThrowUnhandledError_ErrorThrown()
- {
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _fhirTransactionPipeline.When(pipeline => pipeline.ProcessAsync(Arg.Any(), Arg.Any())).Do(pipeline => { throw new Exception(); });
-
- // Act
- await Assert.ThrowsAsync(() => ExecuteProcessAsync());
-
- // Assert
- await _changeFeedRetrieveService.Received(1).RetrieveLatestSequenceAsync(DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task WhenThrowTimeoutRejectedException_ExceptionNotThrown()
- {
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(1L);
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- _fhirTransactionPipeline.When(pipeline => pipeline.ProcessAsync(Arg.Any(), Arg.Any())).Do(pipeline => { throw new TimeoutRejectedException(); });
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(2).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(2).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- }
-
- [Theory]
- [InlineData(nameof(DicomTagException))]
- [InlineData(nameof(MissingRequiredDicomTagException))]
- public async Task WhenThrowDicomTagException_ExceptionNotThrown(string exception)
- {
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(1L);
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- _fhirTransactionPipeline.When(pipeline => pipeline.ProcessAsync(Arg.Any(), Arg.Any())).Do(pipeline => { ThrowDicomTagException(exception); });
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(2).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(2).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task WhenMissingRequiredDicomTagException_ExceptionNotThrown()
- {
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(1L);
-
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- _fhirTransactionPipeline.When(pipeline => pipeline.ProcessAsync(Arg.Any(), Arg.Any())).Do(pipeline => { throw new MissingRequiredDicomTagException(nameof(DicomTag.PatientID)); });
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(2).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(2).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task WhenThrowFhirNonRetryableException_ExceptionNotThrown()
- {
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(1L);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- _fhirTransactionPipeline.When(pipeline => pipeline.ProcessAsync(Arg.Any(), Arg.Any())).Do(pipeline => { throw new FhirNonRetryableException("exception"); });
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _changeFeedRetrieveService.Received(2).RetrieveLatestSequenceAsync(DefaultCancellationToken);
-
- await _changeFeedRetrieveService.ReceivedWithAnyArgs(2).RetrieveChangeFeedAsync(default, default, default);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
-
- await _fhirTransactionPipeline.Received(1).ProcessAsync(changeFeeds1[0], DefaultCancellationToken);
- }
-
- [Fact]
- public async Task GivenMultipleChangeFeedEntries_WhenProcessing_ThenPollIntervalShouldBeHonored()
- {
- var pollIntervalDuringCatchup = TimeSpan.FromMilliseconds(50);
-
- ChangeFeedEntry[] changeFeeds1 = new[]
- {
- ChangeFeedGenerator.Generate(1),
- };
-
- ChangeFeedEntry[] changeFeeds2 = new[]
- {
- ChangeFeedGenerator.Generate(2),
- };
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(2L);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds1);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(1, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds2);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(2, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- var stopwatch = new Stopwatch();
-
- _fhirTransactionPipeline.When(processor => processor.ProcessAsync(changeFeeds1[0], DefaultCancellationToken)).Do(_ => stopwatch.Start());
- _fhirTransactionPipeline.When(processor => processor.ProcessAsync(changeFeeds2[0], DefaultCancellationToken)).Do(_ => stopwatch.Stop());
-
- // Execute Process when no poll interval is defined.
- await ExecuteProcessAsync();
-
- // Using stopwatch.Elapsed to get total time elapsed when no poll interval is defined.
- TimeSpan totalTimeTakenWithNoPollInterval = stopwatch.Elapsed;
-
- stopwatch.Reset();
-
- // Execute process when poll interval is defined.
- await ExecuteProcessAsync(pollIntervalDuringCatchup);
-
- // Using stopwatch.Elapsed to get total time elapsed when poll interval is defined.
- TimeSpan totalTimeTakenWithPollInterval = stopwatch.Elapsed;
-
- Assert.True(totalTimeTakenWithPollInterval >= totalTimeTakenWithNoPollInterval);
- }
-
- [Fact]
- public async Task GivenDefaultState_WhenProcessed_ThenSyncStateShouldNotBeUpdated()
- {
- // Arrange
- _syncStateService.GetSyncStateAsync(DefaultCancellationToken).Returns(SyncState.CreateInitialSyncState());
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(0L);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _syncStateService.Received(1).GetSyncStateAsync(DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveLatestSequenceAsync(DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _fhirTransactionPipeline.DidNotReceiveWithAnyArgs().ProcessAsync(default, default);
- await _syncStateService.DidNotReceiveWithAnyArgs().UpdateSyncStateAsync(default, default);
- }
-
- [Fact]
- public async Task GivenNoChangeFeed_WhenProcessed_ThenSyncStateShouldNotBeUpdated()
- {
- const long Sequence = 27L;
-
- // Arrange
- _syncStateService.GetSyncStateAsync(DefaultCancellationToken).Returns(new SyncState(Sequence, DateTimeOffset.UtcNow));
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(Sequence);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(Sequence, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(Array.Empty());
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _syncStateService.Received(1).GetSyncStateAsync(DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveLatestSequenceAsync(DefaultCancellationToken);
- await _changeFeedRetrieveService.Received(1).RetrieveChangeFeedAsync(Sequence, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken);
- await _fhirTransactionPipeline.DidNotReceiveWithAnyArgs().ProcessAsync(default, default);
- await _syncStateService.DidNotReceiveWithAnyArgs().UpdateSyncStateAsync(default, default);
- }
-
- [Fact]
- public async Task GivenAllChangeFeedEntriesAreSuccess_WhenProcessed_ThenSyncStateShouldBeUpdated()
- {
- const long expectedSequence = 10;
-
- ChangeFeedEntry[] changeFeeds = [ChangeFeedGenerator.Generate(expectedSequence)];
-
- // Arrange
- _changeFeedRetrieveService.RetrieveLatestSequenceAsync(DefaultCancellationToken).Returns(expectedSequence);
- _changeFeedRetrieveService.RetrieveChangeFeedAsync(0, ChangeFeedProcessor.DefaultLimit, DefaultCancellationToken).Returns(changeFeeds);
-
- var instant = DateTimeOffset.UtcNow.AddHours(1);
- _timeProvider.SetUtcNow(instant);
-
- // Act
- await ExecuteProcessAsync();
-
- // Assert
- await _syncStateService
- .Received(1)
- .UpdateSyncStateAsync(Arg.Is(syncState => syncState != null && syncState.SyncedSequence == expectedSequence && syncState.SyncedDate == instant), DefaultCancellationToken);
- }
-
- private void SetupSyncState(long syncedSequence = 0, DateTimeOffset? syncedDate = null)
- {
- var syncState = new SyncState(syncedSequence, syncedDate == null ? DateTimeOffset.MinValue : syncedDate.Value);
-
- _syncStateService.GetSyncStateAsync(DefaultCancellationToken).Returns(syncState);
- }
-
- private async Task ExecuteProcessAsync(TimeSpan? pollIntervalDuringCatchup = null)
- {
- if (pollIntervalDuringCatchup == null)
- {
- pollIntervalDuringCatchup = TimeSpan.Zero;
- }
-
- await _changeFeedProcessor.ProcessAsync(pollIntervalDuringCatchup.Value, DefaultCancellationToken);
- }
-
- private static void ThrowDicomTagException(string exception)
- {
- if (exception.Equals(nameof(DicomTagException)))
- {
- throw new DicomTagException("exception");
- }
- else if (exception.Equals(nameof(MissingRequiredDicomTagException)))
- {
- throw new MissingRequiredDicomTagException(nameof(DicomTag.PatientID));
- }
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/DicomCastWorkerTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/DicomCastWorkerTests.cs
deleted file mode 100644
index 5963ac89f6..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/DicomCastWorkerTests.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker;
-using NSubstitute;
-using OpenTelemetry;
-using OpenTelemetry.Metrics;
-using Xunit;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker;
-
-public class DicomCastWorkerTests
-{
- private const int DefaultNumberOfInvocations = 2;
-
- private readonly DicomCastWorkerConfiguration _dicomCastWorkerConfiguration = new DicomCastWorkerConfiguration();
- private readonly IChangeFeedProcessor _changeFeedProcessor;
- private readonly IHostApplicationLifetime _hostApplication;
- private readonly DicomCastWorker _dicomCastWorker;
- private readonly IFhirService _fhirService;
- private readonly DicomCastMeter _dicomCastMeter;
-
- private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
- private readonly CancellationToken _cancellationToken;
-
- private MeterProvider _meterProvider;
- private List _exportedItems;
-
- public DicomCastWorkerTests()
- {
- _cancellationToken = _cancellationTokenSource.Token;
-
- _dicomCastWorkerConfiguration.PollInterval = TimeSpan.Zero;
-
- _changeFeedProcessor = Substitute.For();
-
- _hostApplication = Substitute.For();
-
- _fhirService = Substitute.For();
-
- _dicomCastMeter = new DicomCastMeter();
-
- _dicomCastWorker = new DicomCastWorker(
- Options.Create(_dicomCastWorkerConfiguration),
- _changeFeedProcessor,
- NullLogger.Instance,
- _hostApplication,
- _fhirService,
- _dicomCastMeter);
-
- InitializeMetricExporter();
- }
-
- [Fact]
- public async Task GivenWorkerIsBeingCanceled_WhenExecuting_ThenWorkerShouldBeCancelled()
- {
- int invocationCount = 0;
-
- _changeFeedProcessor.When(processor => processor.ProcessAsync(_dicomCastWorkerConfiguration.PollIntervalDuringCatchup, _cancellationToken))
- .Do(_ =>
- {
- if (invocationCount++ == DefaultNumberOfInvocations)
- {
- _cancellationTokenSource.Cancel();
-
- throw new TaskCanceledException();
- }
- });
-
- await _dicomCastWorker.ExecuteAsync(_cancellationToken);
-
- await _changeFeedProcessor.Received(invocationCount).ProcessAsync(_dicomCastWorkerConfiguration.PollIntervalDuringCatchup, _cancellationToken);
- }
-
- [Fact]
- public async Task GivenWorkerIsBeingCanceled_WhenExecutingAndFailed_ThenProperMetricsisLogged()
- {
- _changeFeedProcessor.When(processor => processor.ProcessAsync(_dicomCastWorkerConfiguration.PollIntervalDuringCatchup, _cancellationToken))
- .Do(_ =>
- {
- throw new TaskCanceledException();
- });
-
- await _dicomCastWorker.ExecuteAsync(_cancellationToken);
-
- _meterProvider.ForceFlush();
-
- Assert.NotEmpty(_exportedItems.Where(item => item.Name.Equals("CastingFailedForOtherReasons")));
- }
-
- [Fact(Skip = "Flaky test, bug: https://microsofthealth.visualstudio.com/Health/_boards/board/t/Medical%20Imaging/Stories/?workitem=78349")]
- public async Task GivenWorker_WhenExecuting_ThenPollIntervalShouldBeHonored()
- {
- var pollInterval = TimeSpan.FromMilliseconds(50);
-
- _dicomCastWorkerConfiguration.PollInterval = pollInterval;
-
- int invocationCount = 0;
-
- var stopwatch = new Stopwatch();
-
- _changeFeedProcessor.When(processor => processor.ProcessAsync(_dicomCastWorkerConfiguration.PollIntervalDuringCatchup, _cancellationToken))
- .Do(_ =>
- {
- if (invocationCount++ == 0)
- {
- stopwatch.Start();
- }
- else
- {
- stopwatch.Stop();
-
- _cancellationTokenSource.Cancel();
- }
- });
-
- await _dicomCastWorker.ExecuteAsync(_cancellationToken);
-
- Assert.True(stopwatch.ElapsedMilliseconds >= pollInterval.TotalMilliseconds);
- }
-
- private void InitializeMetricExporter()
- {
- _exportedItems = new List();
- _meterProvider = Sdk.CreateMeterProviderBuilder()
- .AddMeter("Microsoft.Health.DicomCast", "1.0")
- .AddInMemoryExporter(_exportedItems)
- .Build();
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/EndpointPipelineStepTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/EndpointPipelineStepTests.cs
deleted file mode 100644
index f531837f70..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/EndpointPipelineStepTests.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Linq;
-using System.Threading;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Extensions;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class EndpointPipelineStepTests
-{
- private const string EndpointConnectionTypeSystem = "http://terminology.hl7.org/CodeSystem/endpoint-connection-type";
- private const string EndpointConnectionTypeCode = "dicom-wado-rs";
- private const string EndpointName = "DICOM WADO-RS endpoint https://dicom/";
- private const string EndpointPayloadTypeText = "DICOM WADO-RS";
- private const string DicomMimeType = "application/dicom";
-
- private const string DefaultDicomWebEndpoint = "https://dicom/";
-
- private static readonly CancellationToken DefaultCancellationToken = new CancellationTokenSource().Token;
-
- private readonly DicomWebConfiguration _configuration;
- private readonly EndpointPipelineStep _endpointPipeline;
- private readonly IFhirService _fhirService;
-
- public EndpointPipelineStepTests()
- {
- _configuration = new DicomWebConfiguration() { Endpoint = new System.Uri(DefaultDicomWebEndpoint), };
-
- IOptions optionsConfiguration = Options.Create(_configuration);
-
- _fhirService = Substitute.For();
-
- _endpointPipeline = new EndpointPipelineStep(optionsConfiguration, _fhirService, NullLogger.Instance);
- }
-
- [Fact]
- public async Task GivenEndpointDoesNotAlreadyExist_WhenRequestIsPrepared_ThenCorrentRequestEntryShouldBeCreated()
- {
- var context = new FhirTransactionContext(ChangeFeedGenerator.Generate());
-
- await _endpointPipeline.PrepareRequestAsync(context, DefaultCancellationToken);
-
- FhirTransactionRequestEntry actualEndpointEntry = context.Request.Endpoint;
-
- ValidationUtility.ValidateRequestEntryMinimumRequirementForWithChange(FhirTransactionRequestMode.Create, "Endpoint", Bundle.HTTPVerb.POST, actualEndpointEntry);
-
- Assert.Equal($"name={EndpointName}&connection-type={EndpointConnectionTypeSystem}|{EndpointConnectionTypeCode}", actualEndpointEntry.Request.IfNoneExist);
-
- Endpoint endpoint = Assert.IsType(actualEndpointEntry.Resource);
-
- Assert.Equal(EndpointName, endpoint.Name);
- Assert.Equal(Endpoint.EndpointStatus.Active, endpoint.Status);
- Assert.NotNull(endpoint.ConnectionType);
- Assert.Equal(EndpointConnectionTypeSystem, endpoint.ConnectionType.System);
- Assert.Equal(EndpointConnectionTypeCode, endpoint.ConnectionType.Code);
- Assert.Equal(_configuration.Endpoint.ToString(), endpoint.Address);
- Assert.Equal(EndpointPayloadTypeText, endpoint.PayloadType.First().Text);
- Assert.Equal(new[] { DicomMimeType }, endpoint.PayloadMimeType);
- }
-
- [Fact]
- public async Task GivenAnExistingEndpointWithMatchingAddress_WhenRequestIsPrepared_ThenCorrectRequestEntryShouldBeCreated()
- {
- var context = new FhirTransactionContext(ChangeFeedGenerator.Generate());
-
- Endpoint endpoint = FhirResourceBuilder.CreateEndpointResource(address: DefaultDicomWebEndpoint);
-
- _fhirService.RetrieveEndpointAsync(Arg.Any(), DefaultCancellationToken).Returns(endpoint);
-
- await _endpointPipeline.PrepareRequestAsync(context, DefaultCancellationToken);
-
- FhirTransactionRequestEntry actualEndPointEntry = context.Request.Endpoint;
-
- ValidationUtility.ValidateRequestEntryMinimumRequirementForNoChange(endpoint.ToServerResourceId(), actualEndPointEntry);
- }
-
- [Fact]
- public async Task GivenAnExistingEndpointWithDifferentAddress_WhenRequestIsPrepared_ThenFhirResourceValidationExceptionShouldBeThrown()
- {
- var context = new FhirTransactionContext(ChangeFeedGenerator.Generate());
-
- Endpoint endpoint = FhirResourceBuilder.CreateEndpointResource(address: "https://dicom2");
-
- _fhirService.RetrieveEndpointAsync(Arg.Any(), DefaultCancellationToken).Returns(endpoint);
-
- await Assert.ThrowsAsync(() => _endpointPipeline.PrepareRequestAsync(context, DefaultCancellationToken));
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirResourceBuilder.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirResourceBuilder.cs
deleted file mode 100644
index 72f38e3174..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirResourceBuilder.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Collections.Generic;
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class FhirResourceBuilder
-{
- public static ImagingStudy CreateNewImagingStudy(string studyInstanceUid, List seriesInstanceUidList, List sopInstanceUidList, string patientResourceId, string source = "defaultSouce")
- {
- // Create a new ImagingStudy
- ImagingStudy study = new ImagingStudy
- {
- Id = "123",
- Status = ImagingStudy.ImagingStudyStatus.Available,
- Subject = new ResourceReference(patientResourceId),
- Meta = new Meta()
- {
- VersionId = "1",
- Source = source,
- },
- };
-
- foreach (string seriesInstanceUid in seriesInstanceUidList)
- {
- ImagingStudy.SeriesComponent series = new ImagingStudy.SeriesComponent()
- {
- Uid = seriesInstanceUid,
- };
-
- foreach (string sopInstanceUid in sopInstanceUidList)
- {
- ImagingStudy.InstanceComponent instance = new ImagingStudy.InstanceComponent()
- {
- Uid = sopInstanceUid,
- };
-
- series.Instance.Add(instance);
- }
-
- study.Series.Add(series);
- }
-
- study.Identifier.Add(IdentifierUtility.CreateIdentifier(studyInstanceUid));
-
- return study;
- }
-
- public static Endpoint CreateEndpointResource(string id = null, string name = null, string connectionSystem = null, string connectionCode = null, string address = null)
- {
- return new Endpoint()
- {
- Id = id ?? "1234",
- Name = name ?? FhirTransactionConstants.EndpointName,
- Status = Endpoint.EndpointStatus.Active,
- ConnectionType = new Coding()
- {
- System = connectionSystem ?? FhirTransactionConstants.EndpointConnectionTypeSystem,
- Code = connectionCode ?? FhirTransactionConstants.EndpointConnectionTypeCode,
- },
- Address = address ?? "https://dicom/",
- };
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionContextBuilder.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionContextBuilder.cs
deleted file mode 100644
index 13dfbda34a..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionContextBuilder.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using FellowOakDicom;
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class FhirTransactionContextBuilder
-{
- public static readonly DateTime DefaultStudyDateTime = new DateTime(1974, 7, 10, 7, 10, 24);
- public static readonly DateTime DefaultSeriesDateTime = new DateTime(1974, 8, 10, 8, 10, 24);
- public const string DefaultSOPClassUID = "4444";
- public const string DefaultStudyDescription = "Study Description";
- public const string DefaultSeriesDescription = "Series Description";
- public const string DefaultModalitiesInStudy = "MODALITY";
- public const string DefaultModality = "MODALITY";
- public const string DefaultSeriesNumber = "1";
- public const string DefaultInstanceNumber = "1";
- public const string DefaultAccessionNumber = "1";
-
- public static DicomDataset CreateDicomDataset(string sopClassUid = null, string studyDescription = null, string seriesDescrition = null, string modalityInStudy = null, string modalityInSeries = null, string seriesNumber = null, string instanceNumber = null, string accessionNumber = null)
- {
- var ds = new DicomDataset(DicomTransferSyntax.ExplicitVRLittleEndian)
- {
- { DicomTag.SOPClassUID, sopClassUid ?? DefaultSOPClassUID },
- { DicomTag.StudyDate, DefaultStudyDateTime },
- { DicomTag.StudyTime, DefaultStudyDateTime },
- { DicomTag.SeriesDate, DefaultSeriesDateTime },
- { DicomTag.SeriesTime, DefaultSeriesDateTime },
- { DicomTag.StudyDescription, studyDescription ?? DefaultStudyDescription },
- { DicomTag.SeriesDescription, seriesDescrition ?? DefaultSeriesDescription },
- { DicomTag.ModalitiesInStudy, modalityInStudy ?? DefaultModalitiesInStudy },
- { DicomTag.Modality, modalityInSeries ?? DefaultModality },
- { DicomTag.SeriesNumber, seriesNumber ?? DefaultSeriesNumber },
- { DicomTag.InstanceNumber, instanceNumber ?? DefaultInstanceNumber },
- { DicomTag.AccessionNumber, accessionNumber ?? DefaultAccessionNumber },
- { DicomTag.StudyInstanceUID, DicomUID.Generate().UID },
- { DicomTag.SeriesInstanceUID, DicomUID.Generate().UID },
- { DicomTag.SOPInstanceUID, DicomUID.Generate().UID },
- };
-
- return ds;
- }
-
- public static FhirTransactionContext DefaultFhirTransactionContext(DicomDataset metadata = null)
- {
- var context = new FhirTransactionContext(ChangeFeedGenerator.Generate(metadata: metadata ?? CreateDicomDataset()))
- {
- UtcDateTimeOffset = TimeSpan.Zero,
- };
-
- context.Request.Patient = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry();
- context.Request.ImagingStudy = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry();
- context.Request.Endpoint = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry();
-
- return context;
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionPipelineTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionPipelineTests.cs
deleted file mode 100644
index f7758a29be..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionPipelineTests.cs
+++ /dev/null
@@ -1,326 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.Dicom.Client.Models;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Exceptions;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using NSubstitute.ExceptionExtensions;
-using Polly.Timeout;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class FhirTransactionPipelineTests
-{
- private readonly IList _fhirTransactionPipelineSteps = new List();
- private readonly FhirTransactionRequestResponsePropertyAccessors _fhirTransactionRequestResponsePropertyAccessors = new FhirTransactionRequestResponsePropertyAccessors();
- private readonly IFhirTransactionExecutor _fhirTransactionExecutor = Substitute.For();
- private readonly IExceptionStore _exceptionStore = Substitute.For();
-
- private readonly FhirTransactionPipeline _fhirTransactionPipeline;
-
- private readonly IFhirTransactionPipelineStep _captureFhirTransactionContextStep = Substitute.For();
-
- private FhirTransactionContext _capturedFhirTransactionContext;
-
- public FhirTransactionPipelineTests()
- {
- // Use this step to capture the context. The same context will be used across all steps.
- _captureFhirTransactionContextStep.When(pipeline => pipeline.PrepareRequestAsync(Arg.Any(), Arg.Any()))
- .Do(callback =>
- {
- FhirTransactionContext context = callback.ArgAt(0);
-
- _capturedFhirTransactionContext = context;
- });
-
- _fhirTransactionPipelineSteps.Add(_captureFhirTransactionContextStep);
-
- RetryConfiguration retryConfiguration = new RetryConfiguration();
- retryConfiguration.TotalRetryDuration = new TimeSpan(0, 0, 15);
-
- _fhirTransactionPipeline = new FhirTransactionPipeline(
- _fhirTransactionPipelineSteps,
- _fhirTransactionRequestResponsePropertyAccessors,
- _fhirTransactionExecutor,
- _exceptionStore,
- Options.Create(retryConfiguration),
- NullLogger.Instance);
- }
-
- [Theory]
- [InlineData(FhirTransactionRequestMode.Create)]
- [InlineData(FhirTransactionRequestMode.Update)]
- public async Task GivenAResourceToProcess_WhenProcessed_ThenTransactionShouldBeExecuted(FhirTransactionRequestMode requestMode)
- {
- // Setup the pipeline step to simulate creating/updating patient.
- var patientRequest = new FhirTransactionRequestEntry(
- requestMode,
- new Bundle.RequestComponent(),
- new ClientResourceId(),
- new Patient());
-
- var pipelineStep = new MockFhirTransactionPipelineStep()
- {
- OnPrepareRequestAsyncCalled = (context, cancellationToken) =>
- {
- context.Request.Patient = patientRequest;
- },
- };
-
- _fhirTransactionPipelineSteps.Add(pipelineStep);
-
- // Setup the transaction executor to return response.
- var responseBundle = new Bundle();
-
- var responseEntry = new Bundle.EntryComponent()
- {
- Response = new Bundle.ResponseComponent(),
- Resource = new Patient(),
- };
-
- responseBundle.Entry.Add(responseEntry);
-
- _fhirTransactionExecutor.ExecuteTransactionAsync(
- Arg.Any(),
- Arg.Any())
- .Returns(call =>
- {
- // Make sure the request bundle is correct.
- Bundle requestBundle = call.ArgAt(0);
-
- Assert.NotNull(requestBundle);
- Assert.Equal(Bundle.BundleType.Transaction, requestBundle.Type);
-
- Assert.Collection(
- requestBundle.Entry,
- entry =>
- {
- Assert.Equal(patientRequest.ResourceId.ToString(), entry.FullUrl);
- Assert.Equal(patientRequest.Request, entry.Request);
- Assert.Equal(patientRequest.Resource, entry.Resource);
- });
-
- return responseBundle;
- });
-
- // Process
- await _fhirTransactionPipeline.ProcessAsync(ChangeFeedGenerator.Generate(), CancellationToken.None);
-
- // The response should have been processed.
- Assert.NotNull(_capturedFhirTransactionContext);
-
- FhirTransactionResponseEntry patientResponse = _capturedFhirTransactionContext.Response.Patient;
-
- Assert.NotNull(patientResponse);
- Assert.Equal(responseEntry.Response, patientResponse.Response);
- Assert.Equal(responseEntry.Resource, patientResponse.Resource);
- }
-
- [Fact]
- public async Task WhenThrowAnExceptionInProcess_ThrowTheSameException()
- {
- var pipelineStep = new MockFhirTransactionPipelineStep()
- {
- OnPrepareRequestAsyncCalled = (context, cancellationToken) =>
- {
- throw new Exception();
- },
- };
-
- _fhirTransactionPipelineSteps.Add(pipelineStep);
-
- // Process
- await Assert.ThrowsAsync(() => _fhirTransactionPipeline.ProcessAsync(ChangeFeedGenerator.Generate(), CancellationToken.None));
- }
-
- [Fact]
- public async Task GivenNoResourceToProcess_WhenProcessed_ThenTransactionShouldBeExecuted()
- {
- // Setup the pipeline step to simulate no requests.
- IFhirTransactionPipelineStep pipelineStep = Substitute.For();
-
- _fhirTransactionPipelineSteps.Add(pipelineStep);
-
- // Process
- await _fhirTransactionPipeline.ProcessAsync(ChangeFeedGenerator.Generate(), CancellationToken.None);
-
- // There should not be any response.
- pipelineStep.DidNotReceiveWithAnyArgs().ProcessResponse(default);
- }
-
- [Fact]
- public async Task GivenResourcesInMixedState_WhenProcessed_ThenOnlyResourceWithChangesShouldBeProcessed()
- {
- // Setup the pipeline step to simulate updating an existing patient.
- FhirTransactionRequestEntry patientRequest = FhirTransactionRequestEntryGenerator.GenerateDefaultUpdateRequestEntry(
- new ServerResourceId(ResourceType.Patient, "p1"));
-
- var patientStep = new MockFhirTransactionPipelineStep()
- {
- OnPrepareRequestAsyncCalled = (context, cancellationToken) =>
- {
- context.Request.Patient = patientRequest;
- },
- };
-
- // Setup the pipeline step to simulate no update to endpoint.
- FhirTransactionRequestEntry endpointRequest = FhirTransactionRequestEntryGenerator.GenerateDefaultNoChangeRequestEntry(
- new ServerResourceId(ResourceType.Endpoint, "123"));
-
- var endpointStep = new MockFhirTransactionPipelineStep()
- {
- OnPrepareRequestAsyncCalled = (context, cancellationToken) =>
- {
- context.Request.Endpoint = endpointRequest;
- },
- };
-
- // Setup the pipeline step to simulate creating a new imaging study.
- FhirTransactionRequestEntry imagingStudyRequest = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry();
-
- var imagingStudyStep = new MockFhirTransactionPipelineStep()
- {
- OnPrepareRequestAsyncCalled = (context, cancellationToken) =>
- {
- context.Request.ImagingStudy = imagingStudyRequest;
- },
- };
-
- _fhirTransactionPipelineSteps.Add(patientStep);
- _fhirTransactionPipelineSteps.Add(endpointStep);
- _fhirTransactionPipelineSteps.Add(imagingStudyStep);
-
- // Setup the transaction executor to return response.
- // The properties will be processed in alphabetical order.
- var responseBundle = new Bundle();
-
- var imagingStudyResponseEntry = new Bundle.EntryComponent()
- {
- Response = new Bundle.ResponseComponent(),
- Resource = new ImagingStudy(),
- };
-
- var patientResponseEntry = new Bundle.EntryComponent()
- {
- Response = new Bundle.ResponseComponent(),
- Resource = new Patient(),
- };
-
- responseBundle.Entry.Add(imagingStudyResponseEntry);
- responseBundle.Entry.Add(patientResponseEntry);
-
- _fhirTransactionExecutor.ExecuteTransactionAsync(
- Arg.Any(),
- Arg.Any())
- .Returns(call =>
- {
- // Make sure the request bundle is correct.
- Bundle requestBundle = call.ArgAt(0);
-
- var expectedEntries = new Bundle.EntryComponent[]
- {
- new Bundle.EntryComponent()
- {
- FullUrl = imagingStudyRequest.ResourceId.ToString(),
- Request = imagingStudyRequest.Request,
- Resource = imagingStudyRequest.Resource,
- },
- new Bundle.EntryComponent()
- {
- FullUrl = "Patient/p1",
- Request = patientRequest.Request,
- Resource = patientRequest.Resource,
- },
- };
-
- Assert.True(
- requestBundle.Entry.Matches(expectedEntries));
-
- return responseBundle;
- });
-
- // Process
- await _fhirTransactionPipeline.ProcessAsync(ChangeFeedGenerator.Generate(), CancellationToken.None);
-
- // The response should have been processed.
- Assert.NotNull(_capturedFhirTransactionContext);
-
- FhirTransactionResponseEntry endpointResponse = _capturedFhirTransactionContext.Response.Endpoint;
-
- FhirTransactionResponseEntry imaingStudyResponse = _capturedFhirTransactionContext.Response.ImagingStudy;
-
- Assert.NotNull(imaingStudyResponse);
- Assert.Same(imaingStudyResponse.Response, imagingStudyResponseEntry.Response);
- Assert.Same(imaingStudyResponse.Resource, imagingStudyResponseEntry.Resource);
-
- Assert.Null(endpointResponse);
-
- FhirTransactionResponseEntry patientResponse = _capturedFhirTransactionContext.Response.Patient;
-
- Assert.NotNull(patientResponse);
- Assert.Same(patientResponse.Response, patientResponseEntry.Response);
- Assert.Same(patientResponse.Resource, patientResponseEntry.Resource);
- }
-
- [Fact]
- public async Task GivenRetryableException_WhenProcessed_ThenItShouldRetry()
- {
- await ExecuteAndValidateRetryThenThrowTimeOut(new RetryableException());
- }
-
- [Fact]
- public async Task GivenNotConflictException_WhenProcessed_ThenItShouldNotRetry()
- {
- await ExecuteAndValidate(new Exception(), 1);
- }
-
- [Fact]
- public async Task GivenHttpRequestExceptionException_ProcessAsync_ShouldRetryRetryableException()
- {
- await ExecuteAndValidateRetryThenThrowTimeOut(new HttpRequestException());
- }
-
- [Fact]
- public async Task GivenTaskCancelledExceptionException_ProcessAsync_ShouldRetryRetryableException()
- {
- await ExecuteAndValidateRetryThenThrowTimeOut(new TaskCanceledException());
- }
-
- private async Task ExecuteAndValidate(Exception ex, int expectedNumberOfCalls)
- {
- ChangeFeedEntry changeFeedEntry = ChangeFeedGenerator.Generate();
- var context = new FhirTransactionContext(changeFeedEntry);
-
- _captureFhirTransactionContextStep.PrepareRequestAsync(Arg.Any(), default).ThrowsForAnyArgs(ex);
-
- await Assert.ThrowsAsync(ex.GetType(), () => _fhirTransactionPipeline.ProcessAsync(changeFeedEntry, default));
-
- await _captureFhirTransactionContextStep.Received(expectedNumberOfCalls).PrepareRequestAsync(Arg.Any(), Arg.Any());
- }
-
- private async Task ExecuteAndValidateRetryThenThrowTimeOut(Exception ex)
- {
- ChangeFeedEntry changeFeedEntry = ChangeFeedGenerator.Generate();
- var context = new FhirTransactionContext(changeFeedEntry);
-
- _captureFhirTransactionContextStep.PrepareRequestAsync(Arg.Any(), Arg.Any()).ThrowsForAnyArgs(ex);
-
- await Assert.ThrowsAsync(() => _fhirTransactionPipeline.ProcessAsync(changeFeedEntry, default));
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestEntryGenerator.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestEntryGenerator.cs
deleted file mode 100644
index bc6ee37d10..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestEntryGenerator.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public static class FhirTransactionRequestEntryGenerator
-{
- public static FhirTransactionRequestEntry GenerateDefaultCreateRequestEntry()
- where TResource : Resource, new()
- {
- return new FhirTransactionRequestEntry(
- FhirTransactionRequestMode.Create,
- new Bundle.RequestComponent()
- {
- Method = Bundle.HTTPVerb.POST,
- },
- new ClientResourceId(),
- new TResource());
- }
-
- public static FhirTransactionRequestEntry GenerateDefaultUpdateRequestEntry(ServerResourceId resourceId)
- where TResource : Resource, new()
- {
- return new FhirTransactionRequestEntry(
- FhirTransactionRequestMode.Update,
- new Bundle.RequestComponent()
- {
- Method = Bundle.HTTPVerb.PUT,
- },
- resourceId,
- new TResource());
- }
-
- public static FhirTransactionRequestEntry GenerateDefaultNoChangeRequestEntry(ServerResourceId resourceId)
- where TResource : Resource, new()
- {
- return new FhirTransactionRequestEntry(
- FhirTransactionRequestMode.None,
- request: null,
- resourceId,
- new TResource());
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestResponsePropertyAccessorTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestResponsePropertyAccessorTests.cs
deleted file mode 100644
index a688867ff7..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestResponsePropertyAccessorTests.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using Xunit;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class FhirTransactionRequestResponsePropertyAccessorTests
-{
- private readonly FhirTransactionRequest _fhirTransactionRequest = new FhirTransactionRequest();
- private readonly FhirTransactionResponse _fhirTransactionResponse = new FhirTransactionResponse();
-
- private readonly FhirTransactionRequestResponsePropertyAccessor _patientPropertyAccessor;
-
- public FhirTransactionRequestResponsePropertyAccessorTests()
- {
- _patientPropertyAccessor = CreatePropertyAccessor();
- }
-
- [Fact]
- public void GiveTheRequestEntryGetter_WhenInvoked_ThenCorrectValueShouldBeReturned()
- {
- FhirTransactionRequestEntry requestEntry = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry();
-
- _fhirTransactionRequest.Patient = requestEntry;
-
- Assert.Same(
- requestEntry,
- _patientPropertyAccessor.RequestEntryGetter(_fhirTransactionRequest).Single());
- }
-
- [Fact]
- public void GiveTheResponseEntryGetter_WhenInvoked_ThenCorrectValueShouldBeSet()
- {
- FhirTransactionResponseEntry responseEntry = new(new Bundle.ResponseComponent(), new Patient());
- var responseEntryList = new List { responseEntry };
-
- _patientPropertyAccessor.ResponseEntrySetter(_fhirTransactionResponse, responseEntryList);
-
- Assert.Same(
- responseEntry,
- _fhirTransactionResponse.Patient);
- }
-
- [Fact]
- public void GivenSamePropertyAccessor_WhenHashCodeIsComputed_ThenHashCodeShouldBeTheSame()
- {
- FhirTransactionRequestResponsePropertyAccessor anotherPatientPropertyAccessor = CreatePropertyAccessor();
-
- Assert.Equal(_patientPropertyAccessor.GetHashCode(), anotherPatientPropertyAccessor.GetHashCode());
- }
-
- [Fact]
- public void GivenPropertyAccessorWithDifferentPropertyName_WhenHashCodeIsComputed_ThenHashCodeShouldBeDifferent()
- {
- FhirTransactionRequestResponsePropertyAccessor anotherPatientPropertyAccessor = CreatePropertyAccessor(
- propertyName: "ImagingStudy");
-
- Assert.NotEqual(_patientPropertyAccessor.GetHashCode(), anotherPatientPropertyAccessor.GetHashCode());
- }
-
- [Fact]
- public void GivenPropertyAccessorWithDifferentRequestEntryGetter_WhenHashCodeIsComputed_ThenHashCodeShouldBeDifferent()
- {
- FhirTransactionRequestResponsePropertyAccessor anotherPatientPropertyAccessor = CreatePropertyAccessor(
- requestEntryGetter: request => new[] { request.ImagingStudy });
-
- Assert.NotEqual(_patientPropertyAccessor.GetHashCode(), anotherPatientPropertyAccessor.GetHashCode());
- }
-
- [Fact]
- public void GivenPropertyAccessorWithDifferentResponseEntrySetter_WhenHashCodeIsComputed_ThenHashCodeShouldBeDifferent()
- {
- FhirTransactionRequestResponsePropertyAccessor anotherPatientPropertyAccessor = CreatePropertyAccessor(
- responseEntrySetter: (response, responseEntry) => response.ImagingStudy = responseEntry.Single());
-
- Assert.NotEqual(_patientPropertyAccessor.GetHashCode(), anotherPatientPropertyAccessor.GetHashCode());
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDefaultUsingObjectEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals((object)default));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToSamePropertyAccessorUsingObjectEquals_ThenTrueShouldBeReturned()
- {
- Assert.True(_patientPropertyAccessor.Equals((object)_patientPropertyAccessor));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenPropertyNameIsDifferentUsingObjectEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(
- (object)CreatePropertyAccessor(propertyName: "ImagingStudy")));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenRequestEntryGetterIsDifferentUsingObjectEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(
- (object)CreatePropertyAccessor(requestEntryGetter: request => new[] { request.ImagingStudy })));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenResponseEntrySetterIsDifferentUsingObjectEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(
- (object)CreatePropertyAccessor(responseEntrySetter: (response, responseEntry) => response.ImagingStudy = responseEntry.Single())));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDefaultUsingEquatableEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(default));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToSamePropertyAccessorUsingEquatableEquals_ThenTrueShouldBeReturned()
- {
- Assert.True(_patientPropertyAccessor.Equals(_patientPropertyAccessor));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenPropertyNameIsDifferentUsingEquatableEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(
- CreatePropertyAccessor(propertyName: "ImagingStudy")));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenRequestEntryGetterIsDifferentUsingEquatableEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(
- CreatePropertyAccessor(requestEntryGetter: request => new[] { request.ImagingStudy })));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenResponseEntrySetterIsDifferentUsingEquatableEquals_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor.Equals(
- CreatePropertyAccessor(responseEntrySetter: (response, responseEntry) => response.ImagingStudy = responseEntry.Single())));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDefaultUsingEqualityOperator_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor == default);
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToSamePropertyAccessorUsingEqualityOperator_ThenTrueShouldBeReturned()
- {
-#pragma warning disable CS1718 // Comparison made to same variable
- Assert.True(_patientPropertyAccessor == _patientPropertyAccessor);
-#pragma warning restore CS1718 // Comparison made to same variable
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenPropertyNameIsDifferentUsingEqualityOperator_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor ==
- CreatePropertyAccessor(propertyName: "ImagingStudy"));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenRequestEntryGetterIsDifferentUsingEqualityOperator_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor ==
- CreatePropertyAccessor(requestEntryGetter: request => new[] { request.ImagingStudy }));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenResponseEntrySetterIsDifferentUsingEqualityOperator_ThenFalseShouldBeReturned()
- {
- Assert.False(_patientPropertyAccessor ==
- CreatePropertyAccessor(responseEntrySetter: (response, responseEntry) => response.ImagingStudy = responseEntry.Single()));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDefaultUsingInequalityOperator_ThenFalseShouldBeReturned()
- {
- Assert.True(_patientPropertyAccessor != default);
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToSamePropertyAccessorUsingInequalityOperator_ThenTrueShouldBeReturned()
- {
-#pragma warning disable CS1718 // Comparison made to same variable
- Assert.False(_patientPropertyAccessor != _patientPropertyAccessor);
-#pragma warning restore CS1718 // Comparison made to same variable
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenPropertyNameIsDifferentUsingInequalityOperator_ThenFalseShouldBeReturned()
- {
- Assert.True(_patientPropertyAccessor !=
- CreatePropertyAccessor(propertyName: "ImagingStudy"));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenRequestEntryGetterIsDifferentUsingInequalityOperator_ThenFalseShouldBeReturned()
- {
- Assert.True(_patientPropertyAccessor !=
- CreatePropertyAccessor(requestEntryGetter: request => new[] { request.ImagingStudy }));
- }
-
- [Fact]
- public void GivenAPropertyAccessor_WhenCheckingEqualToDifferentPropertyAccessorWhenResponseEntrySetterIsDifferentUsingInequalityOperator_ThenFalseShouldBeReturned()
- {
- Assert.True(_patientPropertyAccessor !=
- CreatePropertyAccessor(responseEntrySetter: (response, responseEntry) => response.ImagingStudy = responseEntry.Single()));
- }
-
- private static FhirTransactionRequestResponsePropertyAccessor CreatePropertyAccessor(
- string propertyName = "Patient",
- Func> requestEntryGetter = null,
- Action> responseEntrySetter = null)
- {
- requestEntryGetter ??= request => new List { request.Patient };
- responseEntrySetter ??= (response, responseEntry) => response.Patient = responseEntry.Single();
-
- return new FhirTransactionRequestResponsePropertyAccessor(propertyName, requestEntryGetter, responseEntrySetter);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestResponsePropertyAccessorsTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestResponsePropertyAccessorsTests.cs
deleted file mode 100644
index 36effd40ee..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/FhirTransactionRequestResponsePropertyAccessorsTests.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Collections.Generic;
-using System.Linq;
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using Xunit;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class FhirTransactionRequestResponsePropertyAccessorsTests
-{
- private readonly FhirTransactionRequestResponsePropertyAccessors _transactionRequestResponsePropertyAccessors = new FhirTransactionRequestResponsePropertyAccessors();
-
- private readonly FhirTransactionRequest _fhirTransactionRequest = new FhirTransactionRequest()
- {
- Patient = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry(),
- ImagingStudy = FhirTransactionRequestEntryGenerator.GenerateDefaultUpdateRequestEntry(new ServerResourceId(ResourceType.ImagingStudy, "123")),
- Endpoint = FhirTransactionRequestEntryGenerator.GenerateDefaultNoChangeRequestEntry(new ServerResourceId(ResourceType.Endpoint, "abc")),
- };
-
- private readonly FhirTransactionResponse _fhirTransactionResponse = new FhirTransactionResponse();
-
- [Fact]
- public void GivenAPatientRequest_WhenPropertyGetterIsUsed_ThenCorrectValueShouldBeReturned()
- {
- ExecuteAndValidatePropertyGetter(nameof(FhirTransactionRequest.Patient), _fhirTransactionRequest.Patient);
- }
-
- [Fact]
- public void GivenAPatientResponse_WhenPropertySetterIsUsed_ThenCorrectValueShouldBeSet()
- {
- IEnumerable expectedResponse = ExecutePropertySetter(nameof(FhirTransactionRequest.Patient));
-
- Assert.Same(_fhirTransactionResponse.Patient, expectedResponse.Single());
- Assert.Null(_fhirTransactionResponse.ImagingStudy);
- Assert.Null(_fhirTransactionResponse.Endpoint);
- Assert.Null(_fhirTransactionResponse.Observation);
- }
-
- [Fact]
- public void GivenAnImagingStudyRequest_WhenPropertyGetterIsUsed_ThenCorrectValueShouldBeReturned()
- {
- ExecuteAndValidatePropertyGetter(nameof(FhirTransactionRequest.ImagingStudy), _fhirTransactionRequest.ImagingStudy);
- }
-
- [Fact]
- public void GivenAnImagingStudyResponse_WhenPropertySetterIsUsed_ThenCorrectValueShouldBeSet()
- {
- IEnumerable expectedResponse = ExecutePropertySetter(nameof(FhirTransactionRequest.ImagingStudy));
-
- Assert.Same(_fhirTransactionResponse.ImagingStudy, expectedResponse.Single());
- }
-
- [Fact]
- public void GivenAnEndpointRequest_WhenPropertyGetterIsUsed_ThenCorrectValueShouldBeReturned()
- {
- ExecuteAndValidatePropertyGetter(nameof(FhirTransactionRequest.Endpoint), _fhirTransactionRequest.Endpoint);
- }
-
- [Fact]
- public void GivenAnEndpointResponse_WhenPropertySetterIsUsed_ThenCorrectValueShouldBeSet()
- {
- IEnumerable expectedResponse = ExecutePropertySetter(nameof(FhirTransactionRequest.Endpoint));
-
- Assert.Same(_fhirTransactionResponse.Endpoint, expectedResponse.Single());
- }
-
- private void ExecuteAndValidatePropertyGetter(string propertyName, FhirTransactionRequestEntry expectedEntry)
- {
- FhirTransactionRequestResponsePropertyAccessor propertyAccessor = GetPropertyAccessor(propertyName);
-
- IEnumerable requestEntry = propertyAccessor.RequestEntryGetter(_fhirTransactionRequest);
-
- Assert.Same(expectedEntry, requestEntry.Single());
- }
-
- private IEnumerable ExecutePropertySetter(string propertyName)
- {
- FhirTransactionRequestResponsePropertyAccessor propertyAccessor = GetPropertyAccessor(propertyName);
-
- var expectedResponse = new[] { new FhirTransactionResponseEntry(new Bundle.ResponseComponent(), new Patient()) };
-
- propertyAccessor.ResponseEntrySetter(_fhirTransactionResponse, expectedResponse);
-
- return expectedResponse;
- }
-
- private FhirTransactionRequestResponsePropertyAccessor GetPropertyAccessor(string propertyName)
- => _transactionRequestResponsePropertyAccessors.PropertyAccessors.First(propertyAccessor => propertyAccessor.PropertyName == propertyName);
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyDeleteHandlerTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyDeleteHandlerTests.cs
deleted file mode 100644
index 13e50727cf..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyDeleteHandlerTests.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.Dicom.Client.Models;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudyDeleteHandlerTests
-{
- private const string DefaultDicomWebEndpoint = "https://dicom/";
-
- private readonly IFhirService _fhirService;
- private readonly ImagingStudyDeleteHandler _imagingStudyDeleteHandler;
- private readonly DicomWebConfiguration _configuration;
-
- private FhirTransactionContext _fhirTransactionContext;
-
- public ImagingStudyDeleteHandlerTests()
- {
- _configuration = new DicomWebConfiguration() { Endpoint = new System.Uri(DefaultDicomWebEndpoint), };
- IOptions optionsConfiguration = Options.Create(_configuration);
-
- _fhirService = Substitute.For();
- _imagingStudyDeleteHandler = new ImagingStudyDeleteHandler(_fhirService, optionsConfiguration);
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryToDeleteAnInstanceWithinASeriesContainingMoreThanOneInstance_WhenBuilt_ThenCorrectEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string sopInstanceUid1 = "3";
- const string patientResourceId = "p1";
-
- // create a new ImagingStudy
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid }, new List() { sopInstanceUid, sopInstanceUid1 }, patientResourceId);
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any()).Returns(imagingStudy);
-
- // delete an existing instance within a study
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid, patientResourceId);
-
- ImagingStudy updatedImagingStudy = ValidationUtility.ValidateImagingStudyUpdate(studyInstanceUid, patientResourceId, entry, hasAccessionNumber: false);
-
- Assert.Equal(ImagingStudy.ImagingStudyStatus.Available, updatedImagingStudy.Status);
-
- Assert.Collection(
- updatedImagingStudy.Series,
- series =>
- {
- Assert.Equal(seriesInstanceUid, series.Uid);
-
- Assert.Collection(
- series.Instance,
- instance => Assert.Equal(sopInstanceUid1, instance.Uid));
- });
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryToDeleteAnInstanceWithinASeriesContainingOneInstanceDifferentSouce_WhenBuilt_ShouldUpdateNotDelete()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
-
- // create a new ImagingStudy
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid }, new List() { sopInstanceUid }, patientResourceId);
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any()).Returns(imagingStudy);
-
- // delete an existing instance within a study
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid, patientResourceId);
-
- Assert.Equal(FhirTransactionRequestMode.Update, entry.RequestMode);
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryToDeleteAnInstanceWithinASeriesContainingOneInstanceSameSouce_WhenBuilt_ShouldDelete()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
-
- // create a new ImagingStudy
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid }, new List() { sopInstanceUid }, patientResourceId, DefaultDicomWebEndpoint);
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any()).Returns(imagingStudy);
-
- // delete an existing instance within a study
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid, patientResourceId);
-
- Assert.Equal(FhirTransactionRequestMode.Delete, entry.RequestMode);
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryToDeleteAnInstanceWithinAStudyContainingMoreThanOneSeries_WhenBuilt_ThenCorrectEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string seriesInstanceUid1 = "3";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
-
- // create a new ImagingStudy
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid, seriesInstanceUid1 }, new List() { sopInstanceUid, }, patientResourceId);
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any()).Returns(imagingStudy);
-
- // delete an existing instance within a study
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid, patientResourceId);
-
- ImagingStudy updatedImagingStudy = ValidationUtility.ValidateImagingStudyUpdate(studyInstanceUid, patientResourceId, entry, hasAccessionNumber: false);
-
- Assert.Equal(ImagingStudy.ImagingStudyStatus.Available, updatedImagingStudy.Status);
-
- Assert.Collection(
- updatedImagingStudy.Series,
- series =>
- {
- ValidationUtility.ValidateSeries(series, seriesInstanceUid1, sopInstanceUid);
- });
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryForDeleteInstanceThatDoesNotExistsWithinGivenStudy_WhenBuilt_ThenNoEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string sopInstanceUid1 = "4";
- const string patientResourceId = "p1";
-
- // create a new ImagingStudy
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid }, new List() { sopInstanceUid }, patientResourceId);
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any()).Returns(imagingStudy);
-
- // try delete non-existing instance within a study
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid1, patientResourceId);
-
- Assert.Null(entry);
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryForDeleteForStudyInstanceThatDoesNotExists_WhenBuilt_ThenNoEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
-
- // try delete instance from a non-existing study
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid, patientResourceId: "p1");
-
- Assert.Null(entry);
- }
-
- private async Task BuildImagingStudyEntryComponent(string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid, string patientResourceId)
- {
- ChangeFeedEntry changeFeedEntry = ChangeFeedGenerator.Generate(action: ChangeFeedAction.Delete, studyInstanceUid: studyInstanceUid, seriesInstanceUid: seriesInstanceUid, sopInstanceUid: sopInstanceUid);
- return await PrepareRequestAsync(changeFeedEntry, patientResourceId);
- }
-
- private async Task PrepareRequestAsync(ChangeFeedEntry changeFeedEntry, string patientResourceId)
- {
- _fhirTransactionContext = new FhirTransactionContext(changeFeedEntry);
-
- _fhirTransactionContext.Request.Patient = FhirTransactionRequestEntryGenerator.GenerateDefaultNoChangeRequestEntry(new ServerResourceId(ResourceType.Patient, patientResourceId));
-
- return await _imagingStudyDeleteHandler.BuildAsync(_fhirTransactionContext, CancellationToken.None);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyInstancePropertySynchronizerTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyInstancePropertySynchronizerTests.cs
deleted file mode 100644
index 08df0018e1..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyInstancePropertySynchronizerTests.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using FellowOakDicom;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudyInstancePropertySynchronizerTests
-{
- private const string StudyInstanceUid = "111";
- private const string SeriesInstanceUid = "222";
- private const string SopInstanceUid = "333";
- private const string SopClassUid = "4444";
- private const string PatientResourceId = "555";
-
- private static readonly CancellationToken DefaultCancellationToken = new CancellationTokenSource().Token;
- private readonly IImagingStudyInstancePropertySynchronizer _imagingStudyInstancePropertySynchronizer;
- private readonly DicomCastConfiguration _dicomCastConfig = new DicomCastConfiguration();
- private readonly IExceptionStore _exceptionStore = Substitute.For();
-
- public ImagingStudyInstancePropertySynchronizerTests()
- {
- _imagingStudyInstancePropertySynchronizer = new ImagingStudyInstancePropertySynchronizer(Options.Create(_dicomCastConfig), _exceptionStore);
- }
-
- [Fact]
- public async Task GivenATransactionContexAndImagingStudy_WhenprocessedForInstance_ThenDicomPropertiesAreCorrectlyMappedtoInstanceWithinImagingStudyAsync()
- {
- DicomDataset dataset = FhirTransactionContextBuilder.CreateDicomDataset();
-
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(dataset);
-
- if (imagingStudy.Series.Count() > 0)
- {
- System.Console.WriteLine("");
- }
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
- ImagingStudy.InstanceComponent instance = series.Instance.First();
-
- await _imagingStudyInstancePropertySynchronizer.SynchronizeAsync(context, instance, DefaultCancellationToken);
-
- Assert.Equal(SopClassUid, instance.SopClass.Code);
- Assert.Equal(1, instance.Number);
- }
-
- [Fact]
- public async Task GivenATransactionContextWithUpdatedInstanceNumber_WhenprocessedForInstance_ThenDicomPropertyValuesAreUpdatedCorrectlyAsync()
- {
- DicomDataset dataset = FhirTransactionContextBuilder.CreateDicomDataset();
-
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(dataset);
-
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
- ImagingStudy.InstanceComponent instance = series.Instance.First();
-
- await _imagingStudyInstancePropertySynchronizer.SynchronizeAsync(context, instance, DefaultCancellationToken);
-
- Assert.Equal(1, instance.Number);
-
- FhirTransactionContext newContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(instanceNumber: "2"));
-
- await _imagingStudyInstancePropertySynchronizer.SynchronizeAsync(newContext, instance, DefaultCancellationToken);
- Assert.Equal(2, instance.Number);
- }
-
- [Fact]
- public async Task GivenATransactionContextWithNoDicomPropertyValueChange_WhenprocessedForInstancee_ThenDicomPropertyValuesUpdateIsSkippedAsync()
- {
- DicomDataset dataset = FhirTransactionContextBuilder.CreateDicomDataset();
-
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(dataset);
-
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
- ImagingStudy.InstanceComponent instance = series.Instance.First();
-
- await _imagingStudyInstancePropertySynchronizer.SynchronizeAsync(context, instance, DefaultCancellationToken);
-
- Assert.Equal(1, instance.Number);
-
- FhirTransactionContext newContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext(dataset);
-
- await _imagingStudyInstancePropertySynchronizer.SynchronizeAsync(newContext, instance, DefaultCancellationToken);
- Assert.Equal(1, instance.Number);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPipelineHelperTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPipelineHelperTests.cs
deleted file mode 100644
index 88199799b1..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPipelineHelperTests.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using FellowOakDicom;
-using Hl7.Fhir.Model;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudyPipelineHelperTests
-{
- private const string DefaultStudyInstanceUid = "111";
- private const string DefaultSeriesInstanceUid = "222";
- private const string DefaultSopInstanceUid = "333";
- private const string DefaultPatientResourceId = "555";
-
- private readonly IExceptionStore _exceptionStore = Substitute.For();
-
- [Fact]
- public void GivenAChangeFeedEntryWithInvalidUtcTimeOffset_WhenDateTimeOffsetIsCalculated_ThenInvalidDicomTagValueExceptionIsThrown()
- {
- FhirTransactionContext fhirTransactionContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext();
- fhirTransactionContext.ChangeFeedEntry.Metadata.Add(DicomTag.TimezoneOffsetFromUTC, "0");
-
- Assert.Throws(
- () => ImagingStudyPipelineHelper.SetDateTimeOffSet(fhirTransactionContext));
- }
-
- [Theory]
- [InlineData(14, 0, "+1400")]
- [InlineData(-8, 0, "-0800")]
- [InlineData(-14, 0, "-1400")]
- [InlineData(8, 0, "+0800")]
- [InlineData(0, 0, "+0000")]
- [InlineData(8, 30, "+0830")]
- public void GivenAChangeFeedEntry_WhenDateTimeOffsetIsCalculated_ThenDateTimeOffsetIsSet(int hour, int minute, string dicomValue)
- {
- FhirTransactionContext fhirTransactionContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext();
-
- DateTimeOffset utcTimeZoneOffset = new DateTimeOffset(2020, 1, 1, 0, 0, 0, new TimeSpan(hour, minute, 0));
-
- fhirTransactionContext.ChangeFeedEntry.Metadata.Add(DicomTag.TimezoneOffsetFromUTC, dicomValue);
-
- ImagingStudyPipelineHelper.SetDateTimeOffSet(fhirTransactionContext);
-
- Assert.Equal(utcTimeZoneOffset.Offset, fhirTransactionContext.UtcDateTimeOffset);
- }
-
- [Fact]
- public void GivenAccessionNumber_WhenGetAccessionNumber_ThenReturnsCorrectIdentifier()
- {
- string accessionNumber = "01234";
- var result = ImagingStudyPipelineHelper.GetAccessionNumber(accessionNumber);
- ValidationUtility.ValidateAccessionNumber(null, accessionNumber, result);
- }
-
- [Fact]
- public async Task SyncPropertiesAsync_PartialValidationNotEnabled_ThrowsError()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- Action actionSubstitute = Substitute.For>();
- actionSubstitute.When(x => x.Invoke(imagingStudy, context)).Do(x => throw new InvalidDicomTagValueException("invalid tag", "invalid tag"));
-
- await Assert.ThrowsAsync(() => ImagingStudyPipelineHelper.SynchronizePropertiesAsync(imagingStudy, context, actionSubstitute, false, true, _exceptionStore));
- }
-
- [Fact]
- public async Task SyncPropertiesAsync_PartialValidationEnabledAndPropertyRequired_ThrowsError()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- Action actionSubstitute = Substitute.For>();
- actionSubstitute.When(x => x.Invoke(imagingStudy, context)).Do(x => throw new InvalidDicomTagValueException("invalid tag", "invalid tag"));
-
- await Assert.ThrowsAsync(() => ImagingStudyPipelineHelper.SynchronizePropertiesAsync(imagingStudy, context, actionSubstitute, true, true, _exceptionStore));
- }
-
- [Fact]
- public async Task SyncPropertiesAsync_PartialValidationEnabledAndPropertyNotRequired_NoError()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- Action actionSubstitute = Substitute.For>();
- actionSubstitute.When(x => x.Invoke(imagingStudy, context)).Do(x => throw new InvalidDicomTagValueException("invalid tag", "invalid tag"));
-
- await ImagingStudyPipelineHelper.SynchronizePropertiesAsync(imagingStudy, context, actionSubstitute, false, false, _exceptionStore);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPipelineStepTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPipelineStepTests.cs
deleted file mode 100644
index 27d2561a84..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPipelineStepTests.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System.Net;
-using System.Threading;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Health.Dicom.Client.Models;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudyPipelineStepTests
-{
- private static readonly CancellationToken DefaultCancellationToken = new CancellationTokenSource().Token;
-
- private readonly IImagingStudyDeleteHandler _imagingStudyDeleteHandler;
- private readonly IImagingStudyUpsertHandler _imagingStudyUpsertHandler;
- private readonly ImagingStudyPipelineStep _imagingStudyPipeline;
-
- public ImagingStudyPipelineStepTests()
- {
- _imagingStudyDeleteHandler = Substitute.For();
- _imagingStudyUpsertHandler = Substitute.For();
-
- _imagingStudyPipeline = new ImagingStudyPipelineStep(_imagingStudyUpsertHandler, _imagingStudyDeleteHandler, NullLogger.Instance);
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryForCreate_WhenPreparingTheRequest_ThenCreateHandlerIsCalled()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
-
- ChangeFeedEntry changeFeed = ChangeFeedGenerator.Generate(
- action: ChangeFeedAction.Create,
- studyInstanceUid: studyInstanceUid,
- seriesInstanceUid: seriesInstanceUid,
- sopInstanceUid: sopInstanceUid);
-
- var fhirTransactionContext = new FhirTransactionContext(changeFeed);
-
- await _imagingStudyPipeline.PrepareRequestAsync(fhirTransactionContext, DefaultCancellationToken);
-
- await _imagingStudyUpsertHandler.Received(1).BuildAsync(fhirTransactionContext, DefaultCancellationToken);
- }
-
- [Fact]
- public void GivenARequestToCreateAnImagingStudy_WhenResponseIsOK_ThenResourceConflictExceptionShouldBeThrown()
- {
- var response = new Bundle.ResponseComponent();
-
- response.AddAnnotation(HttpStatusCode.OK);
-
- var context = new FhirTransactionContext(ChangeFeedGenerator.Generate());
-
- context.Request.ImagingStudy = FhirTransactionRequestEntryGenerator.GenerateDefaultCreateRequestEntry();
-
- context.Response.ImagingStudy = new FhirTransactionResponseEntry(response, new ImagingStudy());
-
- Assert.Throws(() => _imagingStudyPipeline.ProcessResponse(context));
- }
-
- [Fact]
- public async Task GivenAChangeFeedEntryForDelete_WhenBuilt_ThenDeleteHandlerIsCalled()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
-
- ChangeFeedEntry changeFeed = ChangeFeedGenerator.Generate(
- action: ChangeFeedAction.Delete,
- studyInstanceUid: studyInstanceUid,
- seriesInstanceUid: seriesInstanceUid,
- sopInstanceUid: sopInstanceUid);
-
- var fhirTransactionContext = new FhirTransactionContext(changeFeed);
-
- fhirTransactionContext.Request.Patient = FhirTransactionRequestEntryGenerator.GenerateDefaultNoChangeRequestEntry(
- new ServerResourceId(ResourceType.Patient, patientResourceId));
-
- await _imagingStudyPipeline.PrepareRequestAsync(fhirTransactionContext, DefaultCancellationToken);
-
- await _imagingStudyDeleteHandler.Received(1).BuildAsync(fhirTransactionContext, DefaultCancellationToken);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPropertySynchronizerTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPropertySynchronizerTests.cs
deleted file mode 100644
index 092975dd14..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyPropertySynchronizerTests.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudyPropertySynchronizerTests
-{
- private const string DefaultStudyInstanceUid = "111";
- private const string DefaultSeriesInstanceUid = "222";
- private const string DefaultSopInstanceUid = "333";
- private const string DefaultPatientResourceId = "555";
- private const string NewAccessionNumber = "2";
- private readonly IImagingStudyPropertySynchronizer _imagingStudyPropertySynchronizer;
-
- private readonly DicomCastConfiguration _dicomCastConfig = new DicomCastConfiguration();
- private readonly IExceptionStore _exceptionStore = Substitute.For();
-
- public ImagingStudyPropertySynchronizerTests()
- {
- _imagingStudyPropertySynchronizer = new ImagingStudyPropertySynchronizer(Options.Create(_dicomCastConfig), _exceptionStore);
- }
-
- [Fact]
- public async Task GivenATransactionContexAndImagingStudy_WhenProcessedForStudy_ThenDicomPropertiesAreCorrectlyMappedtoImagingStudyAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Endpoint,
- reference => string.Equals(reference.Reference, context.Request.Endpoint.Resource.ToString(), StringComparison.Ordinal));
-
- Assert.Collection(
- imagingStudy.Modality,
- modality => string.Equals(modality.Code, "MODALITY", StringComparison.Ordinal));
-
- Assert.Collection(
- imagingStudy.Note,
- note => string.Equals(note.Text.ToString(), "Study Description", StringComparison.Ordinal));
-
- Assert.Collection(
- imagingStudy.Identifier,
- identifier => string.Equals(identifier.Value, $"urn:oid:{DefaultStudyInstanceUid}", StringComparison.Ordinal), // studyinstanceUid
- identifier => string.Equals(identifier.Value, "1", StringComparison.Ordinal)); // accession number
-
- Assert.Equal(new FhirDateTime(1974, 7, 10, 7, 10, 24, TimeSpan.Zero), imagingStudy.StartedElement);
- }
-
- [Fact]
- public async Task GivenATransactionContexAndImagingStudyWithNewModality_WhenProcessedForStudy_ThenNewModalityIsAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Modality,
- modality => string.Equals(modality.Code, "MODALITY", StringComparison.Ordinal));
-
- FhirTransactionContext newConText = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(modalityInStudy: "NEWMODALITY", modalityInSeries: "NEWMODALITY"));
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(newConText, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Modality,
- modality => string.Equals(modality.Code, "MODALITY", StringComparison.Ordinal),
- modality => string.Equals(modality.Code, "NEWMODALITY", StringComparison.Ordinal));
- }
-
- [Fact]
- public async Task GivenATransactionContextAndImagingStudyWithExitsingModality_WhenProcessedForStudy_ThenModalityIsNotAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Modality,
- modality => string.Equals(modality.Code, "MODALITY", StringComparison.Ordinal));
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Modality,
- modality => string.Equals(modality.Code, "MODALITY", StringComparison.Ordinal));
- }
-
- [Fact]
- public async Task GivenATransactionContexAndImagingStudyWithNewAccessionNumber_WhenProcessedForStudy_ThenNewAccessionNumberIsAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Identifier,
- identifier => ValidationUtility.ValidateIdentifier("urn:dicom:uid", $"urn:oid:{DefaultStudyInstanceUid}", identifier), // studyinstanceUid
- identifier => ValidationUtility.ValidateAccessionNumber(null, FhirTransactionContextBuilder.DefaultAccessionNumber, identifier)); // accession number
-
- FhirTransactionContext newConText = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(accessionNumber: NewAccessionNumber));
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(newConText, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Identifier,
- identifier => ValidationUtility.ValidateIdentifier("urn:dicom:uid", $"urn:oid:{DefaultStudyInstanceUid}", identifier), // studyinstanceUid
- identifier => ValidationUtility.ValidateAccessionNumber(null, FhirTransactionContextBuilder.DefaultAccessionNumber, identifier), // accession number
- identifier => ValidationUtility.ValidateAccessionNumber(null, NewAccessionNumber, identifier)); // new accession number
- }
-
- [Fact]
- public async Task GivenATransactionContextAndImagingStudyWithExitsingAccessionNumber_WhenProcessedForStudy_ThenAccessionNumberIsNotAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Identifier,
- identifier => ValidationUtility.ValidateIdentifier("urn:dicom:uid", $"urn:oid:{DefaultStudyInstanceUid}", identifier),
- identifier => ValidationUtility.ValidateAccessionNumber(null, FhirTransactionContextBuilder.DefaultAccessionNumber, identifier));
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Identifier,
- identifier => ValidationUtility.ValidateIdentifier("urn:dicom:uid", $"urn:oid:{DefaultStudyInstanceUid}", identifier),
- identifier => ValidationUtility.ValidateAccessionNumber(null, FhirTransactionContextBuilder.DefaultAccessionNumber, identifier));
- }
-
- [Fact]
- public async Task GivenATransactionContextAndImagingStudyWithNoEndpoint_WhenProcessedForStudy_ThenNewEndpointIsAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Endpoint,
- endPoint => Assert.Equal(context.Request.Endpoint.ResourceId.ToResourceReference(), endPoint));
- }
-
- [Fact]
- public async Task GivenATransactionContextAndImagingStudyWithExistingEndpointReference_WhenProcessedForStudy_ThenEndpointResourceIsNotAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- Endpoint endpoint = FhirResourceBuilder.CreateEndpointResource();
- var endpointResourceId = new ServerResourceId(ResourceType.Endpoint, endpoint.Id);
- var endpointReference = endpointResourceId.ToResourceReference();
-
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
- context.Request.Endpoint = FhirTransactionRequestEntryGenerator.GenerateDefaultNoChangeRequestEntry(endpointResourceId);
-
- imagingStudy.Endpoint.Add(endpointReference);
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Endpoint,
- endPoint => Assert.Equal(endpointReference, endPoint));
- }
-
- [Fact]
- public async Task GivenATransactionContextAndImagingStudyWithNewEndpointReference_WhenProcessedForStudyWithEndpoint_ThenEndpointIsAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
-
- // Simulate the imaging study with an existing endpoint.
- Endpoint existingEndpoint = FhirResourceBuilder.CreateEndpointResource(id: "2345", name: "new wado-rs");
- var existingEndpointResourceId = new ServerResourceId(ResourceType.Endpoint, existingEndpoint.Id);
- var existingEndpointReference = existingEndpointResourceId.ToResourceReference();
-
- imagingStudy.Endpoint.Add(existingEndpointReference);
-
- Endpoint endpoint = FhirResourceBuilder.CreateEndpointResource();
- var endpointResourceId = new ServerResourceId(ResourceType.Endpoint, endpoint.Id);
- var endpointReference = endpointResourceId.ToResourceReference();
-
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
- context.Request.Endpoint = FhirTransactionRequestEntryGenerator.GenerateDefaultNoChangeRequestEntry(endpointResourceId);
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Endpoint,
- endPoint => Assert.Equal(existingEndpointReference, endPoint),
- endPoint => Assert.Equal(endpointReference, endPoint));
- }
-
- [Fact]
- public async Task GivenATransactionContexAndImagingStudyWithNewStudyDescription_WhenProcessedForStudy_ThenNewNoteIsAddedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(DefaultStudyInstanceUid, new List() { DefaultSeriesInstanceUid }, new List() { DefaultSopInstanceUid }, DefaultPatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Note,
- note => string.Equals(note.Text.ToString(), "Study Description", StringComparison.Ordinal));
-
- // When studyDescription is same, note is not added twice
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(context, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Note,
- note => string.Equals(note.Text.ToString(), "Study Description", StringComparison.Ordinal));
-
- // When study description is new, new note is added
- FhirTransactionContext newConText = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(studyDescription: "New Study Description"));
-
- await _imagingStudyPropertySynchronizer.SynchronizeAsync(newConText, imagingStudy);
-
- Assert.Collection(
- imagingStudy.Note,
- note => string.Equals(note.Text.ToString(), "Study Description", StringComparison.Ordinal),
- note => string.Equals(note.Text.ToString(), "New Study Description", StringComparison.Ordinal));
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudySeriesPropertySynchronizerTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudySeriesPropertySynchronizerTests.cs
deleted file mode 100644
index 5d573d783d..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudySeriesPropertySynchronizerTests.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudySeriesPropertySynchronizerTests
-{
- private static readonly CancellationToken DefaultCancellationToken = new CancellationTokenSource().Token;
- private readonly IImagingStudySeriesPropertySynchronizer _imagingStudySeriesPropertySynchronizer;
- private const string StudyInstanceUid = "111";
- private const string SeriesInstanceUid = "222";
- private const string SopInstanceUid = "333";
- private const string PatientResourceId = "555";
-
- private readonly DicomCastConfiguration _dicomCastConfig = new DicomCastConfiguration();
- private readonly IExceptionStore _exceptionStore = Substitute.For();
-
- public ImagingStudySeriesPropertySynchronizerTests()
- {
- _imagingStudySeriesPropertySynchronizer = new ImagingStudySeriesPropertySynchronizer(Options.Create(_dicomCastConfig), _exceptionStore);
- }
-
- [Fact]
- public async Task GivenATransactionContexAndImagingStudy_WhenprocessedForSeries_ThenDicomPropertiesAreCorrectlyMappedtoSeriesWithinImagingStudyAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(context, series, DefaultCancellationToken);
-
- Assert.Equal("Series Description", series.Description);
- Assert.Equal("MODALITY", series.Modality.Code);
- Assert.Equal(1, series.Number);
- Assert.Equal(new FhirDateTime(1974, 8, 10, 8, 10, 24, TimeSpan.Zero), series.StartedElement);
- }
-
- [Fact]
- public async Task GivenATransactionContextWithUpdatedSeriesDescription_WhenprocessedForSeries_ThenDicomPropertyValuesAreUpdatedAreCorrectlyAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(context, series, DefaultCancellationToken);
-
- Assert.Equal("Series Description", series.Description);
-
- FhirTransactionContext newContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(seriesDescrition: "New Series Description"));
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(newContext, series, DefaultCancellationToken);
- Assert.Equal("New Series Description", series.Description);
- }
-
- [Fact]
- public async Task GivenATransactionContextWithUpdatedSeriesModality_WhenprocessedForSeries_ThenDicomPropertyValuesAreUpdatedAreCorrectlyAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(context, series, DefaultCancellationToken);
-
- Assert.Equal("MODALITY", series.Modality.Code);
-
- FhirTransactionContext newContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(modalityInSeries: "NEWMODALITY"));
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(newContext, series, DefaultCancellationToken);
- Assert.Equal("NEWMODALITY", series.Modality.Code);
- }
-
- [Fact]
- public async Task GivenATransactionContextWithUpdatedSeriesNumber_WhenprocessedForSeries_ThenDicomPropertyValuesAreUpdatedAreCorrectlyAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset());
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(context, series, DefaultCancellationToken);
-
- Assert.Equal(1, series.Number);
-
- FhirTransactionContext newContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext(FhirTransactionContextBuilder.CreateDicomDataset(seriesNumber: "2"));
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(newContext, series, DefaultCancellationToken);
- Assert.Equal(2, series.Number);
- }
-
- [Fact]
- public async Task GivenATransactionContextWithNoPropertyValueChange_WhenprocessedForSeries_ThenDicomPropertyValuesUpdateIsSkippedAsync()
- {
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(StudyInstanceUid, new List() { SeriesInstanceUid }, new List() { SopInstanceUid }, PatientResourceId);
- FhirTransactionContext context = FhirTransactionContextBuilder.DefaultFhirTransactionContext();
- ImagingStudy.SeriesComponent series = imagingStudy.Series.First();
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(context, series, DefaultCancellationToken);
-
- Assert.Equal(1, series.Number);
-
- FhirTransactionContext newContext = FhirTransactionContextBuilder.DefaultFhirTransactionContext();
-
- await _imagingStudySeriesPropertySynchronizer.SynchronizeAsync(newContext, series, DefaultCancellationToken);
- Assert.Equal(1, series.Number);
- }
-}
diff --git a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyUpsertHandlerTests.cs b/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyUpsertHandlerTests.cs
deleted file mode 100644
index 0d4543a5ee..0000000000
--- a/converter/dicom-cast/src/Microsoft.Health.DicomCast.Core.UnitTests/Features/Worker/FhirTransaction/ImagingStudy/ImagingStudyUpsertHandlerTests.cs
+++ /dev/null
@@ -1,276 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
-// -------------------------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Hl7.Fhir.Model;
-using Microsoft.Extensions.Options;
-using Microsoft.Health.Dicom.Client.Models;
-using Microsoft.Health.DicomCast.Core.Configurations;
-using Microsoft.Health.DicomCast.Core.Features.ExceptionStorage;
-using Microsoft.Health.DicomCast.Core.Features.Fhir;
-using Microsoft.Health.DicomCast.Core.Features.Worker.FhirTransaction;
-using NSubstitute;
-using Xunit;
-using Task = System.Threading.Tasks.Task;
-
-namespace Microsoft.Health.DicomCast.Core.UnitTests.Features.Worker.FhirTransaction;
-
-public class ImagingStudyUpsertHandlerTests
-{
- private const string DefaultDicomWebEndpoint = "https://dicom/";
-
- private readonly IFhirService _fhirService;
- private readonly IImagingStudySynchronizer _imagingStudySynchronizer;
- private readonly ImagingStudyUpsertHandler _imagingStudyUpsertHandler;
- private readonly DicomWebConfiguration _configuration;
- private readonly DicomCastConfiguration _dicomCastConfig = new DicomCastConfiguration();
- private readonly IExceptionStore _exceptionStore = Substitute.For();
-
- private FhirTransactionContext _fhirTransactionContext;
-
- public ImagingStudyUpsertHandlerTests()
- {
- _configuration = new DicomWebConfiguration() { Endpoint = new System.Uri(DefaultDicomWebEndpoint), };
- IOptions optionsConfiguration = Options.Create(_configuration);
-
- _fhirService = Substitute.For();
- _imagingStudySynchronizer = new ImagingStudySynchronizer(new ImagingStudyPropertySynchronizer(Options.Create(_dicomCastConfig), _exceptionStore), new ImagingStudySeriesPropertySynchronizer(Options.Create(_dicomCastConfig), _exceptionStore), new ImagingStudyInstancePropertySynchronizer(Options.Create(_dicomCastConfig), _exceptionStore));
- _imagingStudyUpsertHandler = new ImagingStudyUpsertHandler(_fhirService, _imagingStudySynchronizer, optionsConfiguration);
- }
-
- [Fact]
- public async Task GivenAValidCreateChangeFeed_WhenBuilt_ThenCorrectEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
-
- ChangeFeedEntry changeFeedEntry = ChangeFeedGenerator.Generate(
- action: ChangeFeedAction.Create,
- studyInstanceUid: studyInstanceUid,
- seriesInstanceUid: seriesInstanceUid,
- sopInstanceUid: sopInstanceUid);
-
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, seriesInstanceUid, sopInstanceUid, patientResourceId);
-
- ValidationUtility.ValidateRequestEntryMinimumRequirementForWithChange(FhirTransactionRequestMode.Create, "ImagingStudy", Bundle.HTTPVerb.POST, entry);
-
- ImagingStudy imagingStudy = Assert.IsType(entry.Resource);
-
- string jsonString;
- jsonString = JsonSerializer.Serialize(entry);
-
- Assert.IsType(entry.ResourceId);
- Assert.Equal(ImagingStudy.ImagingStudyStatus.Available, imagingStudy.Status);
- Assert.Null(entry.Request.IfMatch);
-
- ValidationUtility.ValidateResourceReference("Patient/p1", imagingStudy.Subject);
-
- Assert.Collection(
- imagingStudy.Identifier,
- identifier => ValidationUtility.ValidateIdentifier("urn:dicom:uid", $"urn:oid:{studyInstanceUid}", identifier),
- identifier => ValidationUtility.ValidateAccessionNumber(null, FhirTransactionContextBuilder.DefaultAccessionNumber, identifier));
-
- Assert.Collection(
- imagingStudy.Series,
- series =>
- {
- Assert.Equal(seriesInstanceUid, series.Uid);
-
- Assert.Collection(
- series.Instance,
- instance => Assert.Equal(sopInstanceUid, instance.Uid));
- });
-
- ValidateDicomFilePropertiesAreCorrectlyMapped(imagingStudy, series: imagingStudy.Series.First(), instance: imagingStudy.Series.First().Instance.First());
- }
-
- [Fact]
- public async Task GivenAChangeFeedWithNewSeriesAndInstanceForAnExistingImagingStudy_WhenBuilt_ThenCorrectEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
- const string newSeriesInstanceUid = "3";
- const string newSopInstanceUid = "4";
-
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid }, new List() { sopInstanceUid }, patientResourceId);
-
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any()).Returns(imagingStudy);
-
- // Update an existing ImagingStudy
- FhirTransactionRequestEntry entry = await BuildImagingStudyEntryComponent(studyInstanceUid, newSeriesInstanceUid, newSopInstanceUid, patientResourceId);
-
- ImagingStudy updatedImagingStudy = ValidationUtility.ValidateImagingStudyUpdate(studyInstanceUid, patientResourceId, entry);
-
- Assert.Collection(
- updatedImagingStudy.Series,
- series =>
- {
- ValidationUtility.ValidateSeries(series, seriesInstanceUid, sopInstanceUid);
- },
- series =>
- {
- ValidationUtility.ValidateSeries(series, newSeriesInstanceUid, newSopInstanceUid);
- });
- }
-
- [Fact]
- public async Task GivenAChangeFeedWithNewInstanceForAnExistingSeriesAndImagingStudy_WhenBuilt_ThenCorrectEntryComponentShouldBeCreated()
- {
- const string studyInstanceUid = "1";
- const string seriesInstanceUid = "2";
- const string sopInstanceUid = "3";
- const string patientResourceId = "p1";
- const string newSopInstanceUid = "4";
-
- // create a new ImagingStudy
- ImagingStudy imagingStudy = FhirResourceBuilder.CreateNewImagingStudy(studyInstanceUid, new List() { seriesInstanceUid }, new List() { sopInstanceUid }, patientResourceId);
-
- _fhirService.RetrieveImagingStudyAsync(Arg.Any(), Arg.Any