diff --git a/.github/actions/analyze.yml b/.github/actions/analyze.yml
new file mode 100644
index 0000000000..5ad0110a60
--- /dev/null
+++ b/.github/actions/analyze.yml
@@ -0,0 +1,201 @@
+parameters:
+ analyzeARMTemplates: true
+ analyzeBinaries: true
+ analyzePackages: true
+ runAntiMalware: true
+ credScanDirectory: '$(Build.SourcesDirectory)'
+
+steps:
+- task: UseDotNet@2
+ displayName: Use .NET Core sdk
+ inputs:
+ useGlobalJson: true
+
+- ${{ if eq(parameters.analyzeBinaries, 'true') }}:
+ - task: DownloadBuildArtifacts@0
+ displayName: 'Download Binaries'
+ inputs:
+ buildType: 'current'
+ downloadType: 'single'
+ downloadPath: '$(Agent.TempDirectory)/artifacts'
+ artifactName: 'deploy'
+
+- ${{ if eq(parameters.analyzePackages, 'true') }}:
+ - task: DownloadBuildArtifacts@0
+ displayName: 'Download NuGet Packages'
+ inputs:
+ buildType: 'current'
+ downloadType: 'single'
+ downloadPath: '$(Build.SourcesDirectory)/artifacts'
+ artifactName: 'nuget'
+
+- ${{ if eq(parameters.analyzeBinaries, 'true') }}:
+ - task: ExtractFiles@1
+ displayName: 'Extract Stu3 Web Server Binaries'
+ inputs:
+ archiveFilePatterns: '$(Agent.TempDirectory)/artifacts/deploy/Microsoft.Health.Fhir.Stu3.Web.zip'
+ destinationFolder: '$(Build.SourcesDirectory)/artifacts/web/Stu3'
+ - task: ExtractFiles@1
+ displayName: 'Extract R4 Web Server Binaries'
+ inputs:
+ archiveFilePatterns: '$(Agent.TempDirectory)/artifacts/deploy/Microsoft.Health.Fhir.R4.Web.zip'
+ destinationFolder: '$(Build.SourcesDirectory)/artifacts/web/r4'
+ - task: ExtractFiles@1
+ displayName: 'Extract R4B Web Server Binaries'
+ inputs:
+ archiveFilePatterns: '$(Agent.TempDirectory)/artifacts/deploy/Microsoft.Health.Fhir.R4B.Web.zip'
+ destinationFolder: '$(Build.SourcesDirectory)/artifacts/web/r4b'
+ - task: ExtractFiles@1
+ displayName: 'Extract R5 Web Server Binaries'
+ inputs:
+ archiveFilePatterns: '$(Agent.TempDirectory)/artifacts/deploy/Microsoft.Health.Fhir.R5.Web.zip'
+ destinationFolder: '$(Build.SourcesDirectory)/artifacts/web/r5'
+
+- ${{ 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: TemplateAnalyzer@3
+ displayName: 'Run Template Analyzer'
+ inputs:
+ ToolVersion: Latest
+ AnalyzeDirectory: '$(Build.SourcesDirectory)/samples/templates'
+ Verbose: false
+ IncludeNonSecurityRules: true
+
+- task: CredScan@3
+ inputs:
+ scanFolder: ${{ parameters.credScanDirectory }}
+ outputFormat: 'pre'
+ suppressionsFile: 'CredScanSuppressions.json'
+ verboseOutput: true
+
+- task: CSRF@1
+ inputs:
+ Path: '$(Build.SourcesDirectory)'
+ ToolVersion: Latest
+
+- task: Trivy@1
+ displayName: 'Run Trivy'
+ inputs:
+ Target: '$(Build.SourcesDirectory)/build/docker'
+ Severities: all
+ VulTypes: all
+
+- task: PSScriptAnalyzer@1
+ displayName: 'Run PSScriptAnalyzer'
+ inputs:
+ Path: '$(Build.SourcesDirectory)'
+ Settings: required
+ IgnorePattern: .gdn
+ Recurse: true
+
+- task: RoslynAnalyzers@3
+ inputs:
+ userProvideBuildInfo: 'msBuildInfo'
+ msBuildArchitecture: 'DotNetCore'
+ msBuildCommandline: 'dotnet build $(Build.SourcesDirectory)/Microsoft.Health.Fhir.sln --configuration $(buildConfiguration) -p:ContinuousIntegrationBuild=true -f net8.0'
+
+- task: BinSkim@4
+ inputs:
+ toolVersion: Latest
+ InputType: Basic
+ Function: analyze
+ AnalyzeTargetGlob: 'f|$(Agent.TempDirectory)/artifacts/**/*Microsoft.Health.*.dll'
+
+ ## PoliCheck@2 does not need to be added since it is run internally
+
+ ## Tools that are no longer supported:
+ # AutoApplicability@1, CodeMetrics@1, VulnerabilityAssessment@0
+
+- task: SdtReport@2
+ condition: succeededOrFailed()
+ continueOnError: True
+ inputs:
+ GdnExportAllTools: false
+ GdnExportGdnToolArmory: ${{ eq(parameters.analyzeARMTemplates, 'true') }}
+ GdnExportGdnToolCredScan: true
+ GdnExportGdnToolCSRF: true
+ GdnExportGdnToolRoslynAnalyzers: true
+ BinSkim: true
+ CredScan: true
+
+- task: PublishSecurityAnalysisLogs@3
+ condition: succeededOrFailed()
+ continueOnError: True
+ 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: true
+ ESLint: false
+ Flawfinder: false
+ FortifySCA: false
+ FxCop: false
+ ModernCop: false
+ MSRD: false
+ PoliCheck: false
+ RoslynAnalyzers: true
+ SDLNativeRules: false
+ Semmle: false
+ SpotBugs: false
+ TSLint: false
+ WebScout: false
+ ToolLogsNotFoundAction: 'Standard'
+
+- task: PostAnalysis@2
+ condition: succeededOrFailed()
+ inputs:
+ GdnBreakAllTools: false
+ GdnBreakGdnToolArmory: ${{ eq(parameters.analyzeARMTemplates, 'true') }}
+ GdnBreakGdnToolCredScan: true
+ GdnBreakGdnToolCSRF: true
+ GdnBreakGdnToolRoslynAnalyzers: true
+ BinSkim: true
+ CredScan: true
+
+- task: TSAUpload@2
+ condition: and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/main'))
+ displayName: 'TSA upload'
+ inputs:
+ tsaVersion: 'TsaV2'
+ codebase: 'NewOrUpdate'
+ GdnPublishTsaOnboard: false
+ GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)\build\jobs\tsaconfig.gdntsa'
+ GdnPublishTsaExportedResultsPublishable: true
+
+- task: DeleteFiles@1
+ displayName: 'Delete files to make space'
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)'
+ Contents: '**\*'
+
+- task: DropValidatorTask@0
+ displayName: 'SBOM Validator and Publisher Task'
+ inputs:
+ BuildDropPath: '$(Agent.TempDirectory)/artifacts/deploy'
+ OutputPath: 'output.json'
+ ValidateSignature: true
+ Verbosity: 'Verbose'
+ continueOnError: true
diff --git a/.github/actions/build.yml b/.github/actions/build.yml
new file mode 100644
index 0000000000..8bb12db04d
--- /dev/null
+++ b/.github/actions/build.yml
@@ -0,0 +1,88 @@
+parameters:
+ # Default values
+ unitTest: true
+ codeCoverage: false
+ componentGovernance: false
+ packageArtifacts: false
+ packageIntegrationTests: false
+ targetBuildFramework: ''
+
+steps:
+- task: UseDotNet@2
+ displayName: 'Use .NET SDK'
+ inputs:
+ useGlobalJson: true
+
+- ${{ if eq(parameters.targetBuildFramework, '') }}:
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet build $(buildConfiguration)'
+ inputs:
+ command: build
+ arguments: '--configuration $(buildConfiguration) -p:ContinuousIntegrationBuild=true -p:AssemblyVersion="$(assemblySemVer)" -p:FileVersion="$(assemblySemFileVer)" -p:InformationalVersion="$(informationalVersion)" -p:Version="$(majorMinorPatch)" -warnaserror'
+ workingDirectory: $(System.DefaultWorkingDirectory)
+
+- ${{ if ne(parameters.targetBuildFramework, '') }}:
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet build $(buildConfiguration)'
+ inputs:
+ command: build
+ arguments: '--configuration $(buildConfiguration) -p:ContinuousIntegrationBuild=true -p:AssemblyVersion="$(assemblySemVer)" -p:FileVersion="$(assemblySemFileVer)" -p:InformationalVersion="$(informationalVersion)" -p:Version="$(majorMinorPatch)" -warnaserror -f ${{parameters.targetBuildFramework}}'
+ workingDirectory: $(System.DefaultWorkingDirectory)
+
+- ${{ if eq(parameters.unitTest, 'true') }}:
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet test'
+ inputs:
+ command: test
+ projects: '**/*UnitTests/*.csproj'
+ arguments: '--configuration $(buildConfiguration) --no-build -f ${{parameters.targetBuildFramework}}'
+ testRunTitle: 'Unit Tests'
+
+- ${{ if eq(parameters.codeCoverage, 'true') }}:
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet test with coverage'
+ inputs:
+ command: test
+ projects: '**/*UnitTests/*.csproj'
+ arguments: '--configuration $(buildConfiguration) --no-build --collect "XPlat Code Coverage" -s "$(build.sourcesDirectory)/CodeCoverage.runsettings" -v normal -f ${{parameters.targetBuildFramework}}'
+ testRunTitle: 'Unit Tests'
+ - task: reportgenerator@5
+ displayName: 'aggregate code coverage'
+ condition: succeededOrFailed()
+ inputs:
+ reports: '$(Agent.TempDirectory)/*/coverage.cobertura.xml'
+ reporttypes: 'Cobertura'
+ targetdir: '$(Agent.TempDirectory)/coverage'
+ - task: PublishCodeCoverageResults@1
+ displayName: 'publish code coverage'
+ condition: succeededOrFailed()
+ inputs:
+ codeCoverageTool: 'Cobertura'
+ failIfCoverageEmpty: true
+ summaryFileLocation: '$(Agent.TempDirectory)/coverage/Cobertura.xml'
+ - task: PublishBuildArtifacts@1
+ displayName: 'publish Cobertura.xml'
+ inputs:
+ pathToPublish: '$(Agent.TempDirectory)/coverage/Cobertura.xml'
+ artifactName: 'IntegrationTests'
+ artifactType: 'container'
+
+- ${{ if eq(parameters.packageArtifacts, 'true') }}:
+ # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/secure-supply-chain/ado-sbom-generator
+ - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
+ displayName: 'SBOM Generation Task'
+ inputs:
+ BuildDropPath: '$(build.artifactStagingDirectory)'
+ BuildComponentPath: '$(Build.SourcesDirectory)'
+ - task: PublishBuildArtifacts@1
+ displayName: 'Publish SBOM Artifacts'
+ inputs:
+ pathToPublish: '$(build.artifactStagingDirectory)'
+ artifactName: 'deploy'
+ artifactType: 'container'
+
+- ${{ if eq(parameters.packageArtifacts, 'true') }}:
+ - template: package.yml
+
+- ${{ if eq(parameters.packageIntegrationTests, 'true') }}:
+ - template: package-integration-tests.yml
diff --git a/.github/actions/clean-storage-accounts/action.yml b/.github/actions/clean-storage-accounts/action.yml
new file mode 100644
index 0000000000..35df072a4c
--- /dev/null
+++ b/.github/actions/clean-storage-accounts/action.yml
@@ -0,0 +1,30 @@
+name: clean storage Accounts
+description: Removes blob containers from test storage accounts
+
+inputs:
+ environmentName:
+ description: Deployment environment name
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Clean Storage Accounts
+ uses: azure/powershell@v2
+ with:
+ azPSVersion: "latest"
+ inlineScript: |
+ $currentUtcTime = [DateTime]::UtcNow
+ Get-AzContext
+ $storageAccounts = Get-AzStorageAccount -ResourceGroupName ${{ inputs.environmentName }}
+ foreach ($storageAccount in $storageAccounts) {
+
+ $storageContainers = Get-AzStorageContainer -Name * -Context $storageAccount.Context
+ foreach ($container in $storageContainers) {
+ $ageDiff = $currentUtcTime - $container.CloudBlobContainer.Properties.LastModified.UtcDateTime
+ if($ageDiff.TotalDays -ge 3) {
+ Write-Host "Deleting container $($container.Name)"
+ $container.CloudBlobContainer.Delete()
+ }
+ }
+ }
diff --git a/.github/actions/cleanup-integration-test-databases/action.yml b/.github/actions/cleanup-integration-test-databases/action.yml
new file mode 100644
index 0000000000..993c861dfb
--- /dev/null
+++ b/.github/actions/cleanup-integration-test-databases/action.yml
@@ -0,0 +1,25 @@
+name: cleanup integration test databases
+description: Deletes databases used for integration tests from previous runs
+
+inputs:
+ environmentName:
+ description: Deployment environment name
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Remove Integration Test Databases
+ uses: azure/powershell@v1
+ with:
+ azPSVersion: "latest"
+ inlineScript: |
+ Get-AzContext
+ $testNamePatterns = @("SNAPSHOT*","FHIRCOMPATIBILITYTEST*","FHIRINTEGRATIONTEST*","FHIRRESOURCECHANGEDISABLEDTEST*","BASE*","SNAPSHOT*")
+ foreach ($pattern in $testNamePatterns) {
+ $resources = Get-AzResource -ResourceGroupName ${{ inputs.environmentName }} -ResourceType 'Microsoft.Sql/servers/databases' -Name $pattern
+ foreach ($resource in $resources) {
+ Write-Host "Cleaning up $($resource.ResourceName)"
+ Remove-AzResource -ResourceId $resource.ResourceId -Force
+ }
+ }
diff --git a/.github/actions/docker-add-tag.yml b/.github/actions/docker-add-tag.yml
new file mode 100644
index 0000000000..031ae5e484
--- /dev/null
+++ b/.github/actions/docker-add-tag.yml
@@ -0,0 +1,28 @@
+
+parameters:
+- name: sourceTag
+ type: string
+- name: targetTag
+ type: string
+
+jobs:
+- job: DockerAddTag
+ pool:
+ name: '$(DefaultLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - task: AzureCLI@2
+ displayName: 'Azure CLI: InlineScript'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ scriptType: bash
+ scriptLocation: inlineScript
+ inlineScript: |
+ az acr login -n $(azureContainerRegistry)
+ for v in stu3 r4 r4b r5; do
+ sourceImage="$(azureContainerRegistry)/${v}_fhir-server:${{parameters.sourceTag}}"
+ targetImage="$(azureContainerRegistry)/${v}_fhir-server:${{parameters.targetTag}}"
+ docker pull $sourceImage
+ docker tag $sourceImage $targetImage
+ docker push $targetImage
+ done
diff --git a/.github/actions/docker-add-tag/action.yml b/.github/actions/docker-add-tag/action.yml
new file mode 100644
index 0000000000..68d89b1835
--- /dev/null
+++ b/.github/actions/docker-add-tag/action.yml
@@ -0,0 +1,36 @@
+name: Docker Add Main tag
+description: 'Adds the main tag to the images for all supported FHIR versions'
+
+inputs:
+ sourceTag:
+ description: 'The tag to apply to the images'
+ required: true
+ targetTag:
+ description: 'The tag to apply to the images'
+ required: true
+ fhirSchemaVersion:
+ description: 'The FHIR schema version to package'
+ required: true
+ azureContainerRegistry:
+ description: 'The Azure Container Registry to push the images to'
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Azure Login
+ uses: azure/login@v2
+ with:
+ client-id: ${{secrets.AZURE_CLIENT_ID}}
+ subscription-id: ${{secrets.AZURE_SUBSCRIPTION_ID}}
+ tenant-id: ${{secrets.AZURE_TENANT_ID}}
+ enable-AzPSSession: true
+ - name: Add Tag to Docker Images
+ shell: bash
+ run: |
+ az acr login -n ${{inputs.azureContainerRegistry}}
+ sourceImage="${{inputs.azureContainerRegistry}}/${{inputs.fhirSchemaVersion}}_fhir-server:${{inputs.sourceTag}}"
+ targetImage="${{inputs.azureContainerRegistry}}/${{inputs.fhirSchemaVersion}}_fhir-server:${{inputs.targetTag}}"
+ docker pull $sourceImage
+ docker tag $sourceImage $targetImage
+ docker push $targetImage
diff --git a/.github/actions/docker-build-all.yml b/.github/actions/docker-build-all.yml
new file mode 100644
index 0000000000..3f6e766c53
--- /dev/null
+++ b/.github/actions/docker-build-all.yml
@@ -0,0 +1,27 @@
+# DESCRIPTION:
+# Builds and pushes images for all supported FHIR versions
+
+parameters:
+- name: tag
+ type: string
+
+jobs:
+- template: docker-build-push.yml
+ parameters:
+ version: "R4"
+ tag: ${{parameters.tag}}
+
+- template: docker-build-push.yml
+ parameters:
+ version: "R4B"
+ tag: ${{parameters.tag}}
+
+- template: docker-build-push.yml
+ parameters:
+ version: "Stu3"
+ tag: ${{parameters.tag}}
+
+- template: docker-build-push.yml
+ parameters:
+ version: "R5"
+ tag: ${{parameters.tag}}
diff --git a/.github/actions/docker-build-push.yml b/.github/actions/docker-build-push.yml
new file mode 100644
index 0000000000..77cacb2f94
--- /dev/null
+++ b/.github/actions/docker-build-push.yml
@@ -0,0 +1,40 @@
+# DESCRIPTION:
+# Builds and pushes a docker image for a given FHIR version
+
+parameters:
+- name: version
+ type: string
+- name: tag
+ type: string
+
+jobs:
+- job: '${{parameters.version}}_Docker'
+ pool:
+ name: '$(DefaultLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - task: DockerCompose@0
+ displayName: 'Build FHIR ${{parameters.version}} Server Image'
+ inputs:
+ action: Build services
+ azureSubscriptionEndpoint: $(azureSubscriptionEndpoint)
+ azureContainerRegistry: $(azureContainerRegistry)
+ dockerComposeFile: $(composeLocation)
+ dockerComposeFileArgs: |
+ FHIR_VERSION=${{parameters.version}}
+ ASSEMBLY_VER=$(assemblySemFileVer)
+ projectName: ${{parameters.version}}
+ additionalImageTags: ${{parameters.tag}}
+
+ - task: DockerCompose@0
+ displayName: 'Push FHIR ${{parameters.version}} Server Image'
+ inputs:
+ action: Push services
+ azureSubscriptionEndpoint: $(azureSubscriptionEndpoint)
+ azureContainerRegistry: $(azureContainerRegistry)
+ dockerComposeFile: $(composeLocation)
+ dockerComposeFileArgs: |
+ FHIR_VERSION=${{parameters.version}}
+ ASSEMBLY_VER=$(assemblySemFileVer)
+ projectName: ${{parameters.version}}
+ additionalImageTags: ${{parameters.tag}}
diff --git a/.github/actions/docker-build/action.yml b/.github/actions/docker-build/action.yml
new file mode 100644
index 0000000000..1ce5a0ec13
--- /dev/null
+++ b/.github/actions/docker-build/action.yml
@@ -0,0 +1,23 @@
+name: Docker Build
+description: 'Builds images for all supported FHIR versions'
+
+inputs:
+ fhirSchemaVersion:
+ description: 'The FHIR schema version to package'
+ required: true
+ assemblyVersion:
+ description: 'The assembly version to use'
+ required: true
+ composeLocation:
+ description: 'The location of the docker-compose file'
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Build Docker Image
+ shell: bash
+ run: |
+ echo "Building and pushing Docker images for FHIR schema version ${{inputs.fhirSchemaVersion}}"
+ cd build/docker
+ docker-compose build --build-arg FHIR_VERSION=${{inputs.fhirSchemaVersion}} --build-arg ASSEMBLY_VER=${{inputs.assemblyVersion}}
diff --git a/.github/actions/dotnet-build/action.yml b/.github/actions/dotnet-build/action.yml
new file mode 100644
index 0000000000..01d875b7e3
--- /dev/null
+++ b/.github/actions/dotnet-build/action.yml
@@ -0,0 +1,29 @@
+name: dotnet build
+description: Builds the packages and ensures their quality by running tests.
+inputs:
+ assemblyVersion:
+ description: The scaler assembly's version.
+ required: true
+ buildConfiguration:
+ default: Debug
+ description: The dotnet build configuration.
+ required: false
+ fileVersion:
+ description: The scaler assembly's file version.
+ required: true
+ informationalVersion:
+ description: The scaler assembly's informational version.
+ required: true
+ majorMinorPatch:
+ description: The major.minor.patch version to use.
+ required: true
+ dotnet-version:
+ description: 'The version of the .NET SDK to use'
+ required: true
+ default: '8.0.202' # Default version if not specified
+runs:
+ using: composite
+ steps:
+ - name: Build
+ shell: bash
+ run: dotnet build "./Microsoft.Health.Fhir.sln" --configuration ${{inputs.buildConfiguration}} "-p:ContinuousIntegrationBuild=true;AssemblyVersion=${{inputs.assemblyVersion}};FileVersion=${{inputs.fileVersion}};InformationalVersion=${{inputs.informationalVersion}};Version=${{inputs.majorMinorPatch}}" -warnaserror
diff --git a/.github/actions/dotnet-test-core/action.yml b/.github/actions/dotnet-test-core/action.yml
new file mode 100644
index 0000000000..bf3fef1f6f
--- /dev/null
+++ b/.github/actions/dotnet-test-core/action.yml
@@ -0,0 +1,12 @@
+name: dotnet test
+description: 'Runs the unit tests for the Fhir solution'
+inputs:
+ buildConfiguration:
+ description: 'The build configuration to use'
+ required: true
+runs:
+ using: 'composite'
+ steps:
+ - name: Run Unit Tests
+ shell: bash
+ run: dotnet test "Microsoft.Health.Fhir.sln" -p:ContinuousIntegrationBuild=true; --filter "FullyQualifiedName~Core.UnitTests" --configuration ${{inputs.buildConfiguration}} --no-build --verbosity normal
diff --git a/.github/actions/dotnet-test-web/action.yml b/.github/actions/dotnet-test-web/action.yml
new file mode 100644
index 0000000000..1d6f42ff06
--- /dev/null
+++ b/.github/actions/dotnet-test-web/action.yml
@@ -0,0 +1,12 @@
+name: dotnet test
+description: 'Runs the unit tests for the Fhir solution'
+inputs:
+ buildConfiguration:
+ description: 'The build configuration to use'
+ required: true
+runs:
+ using: 'composite'
+ steps:
+ - name: Run Unit Tests
+ shell: bash
+ run: dotnet test "Microsoft.Health.Fhir.sln" -p:ContinuousIntegrationBuild=true; --filter "FullyQualifiedName~Web.UnitTests" --configuration ${{inputs.buildConfiguration}} --no-build --verbosity normal
diff --git a/.github/actions/e2e-setup.yml b/.github/actions/e2e-setup.yml
new file mode 100644
index 0000000000..33890f84d2
--- /dev/null
+++ b/.github/actions/e2e-setup.yml
@@ -0,0 +1,23 @@
+steps:
+ - task: DownloadBuildArtifacts@0
+ inputs:
+ buildType: 'current'
+ downloadType: 'single'
+ downloadPath: '$(System.ArtifactsDirectory)'
+ artifactName: 'IntegrationTests'
+
+ - task: UseDotNet@2
+ inputs:
+ useGlobalJson: true
+
+ - task: AzureKeyVault@1
+ displayName: 'Azure Key Vault: resolute-oss-tenant-info'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ KeyVaultName: 'resolute-oss-tenant-info'
+
+ - task: AzureKeyVault@1
+ displayName: 'Azure Key Vault: $(DeploymentEnvironmentName)-ts'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ KeyVaultName: '$(DeploymentEnvironmentName)-ts'
diff --git a/.github/actions/e2e-tests-extract.yml b/.github/actions/e2e-tests-extract.yml
new file mode 100644
index 0000000000..1d6a11bf0e
--- /dev/null
+++ b/.github/actions/e2e-tests-extract.yml
@@ -0,0 +1,10 @@
+parameters:
+- name: version
+ type: string
+
+steps:
+ - task: ExtractFiles@1
+ displayName: 'Extract E2E Test Binaries'
+ inputs:
+ archiveFilePatterns: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Fhir.${{ parameters.version }}.Tests.E2E.zip'
+ destinationFolder: '$(Agent.TempDirectory)/E2ETests/'
diff --git a/.github/actions/e2e-tests.yml b/.github/actions/e2e-tests.yml
new file mode 100644
index 0000000000..b69ef2351d
--- /dev/null
+++ b/.github/actions/e2e-tests.yml
@@ -0,0 +1,139 @@
+parameters:
+- name: version
+ type: string
+- name: appServiceName
+ type: string
+- name: appServiceType
+ type: string
+
+steps:
+ - template: e2e-tests-extract.yml
+ parameters:
+ version: ${{parameters.version}}
+
+ - task: AzurePowerShell@4
+ displayName: 'Set Variables'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ azurePowerShellVersion: latestVersion
+ ScriptType: inlineScript
+ Inline: |
+ $keyVault = "$(DeploymentEnvironmentName)-ts"
+ $secrets = Get-AzKeyVaultSecret -VaultName $keyVault
+
+ foreach($secret in $secrets)
+ {
+ $environmentVariableName = $secret.Name.Replace("--","_")
+
+ $secretValue = Get-AzKeyVaultSecret -VaultName $keyVault -Name $secret.Name
+ # Replace with -AsPlainText flag when v5.3 of the Az Module is supported
+ $plainValue = ([System.Net.NetworkCredential]::new("", $secretValue.SecretValue).Password).ToString()
+ if([string]::IsNullOrEmpty($plainValue))
+ {
+ throw "$($secret.Name) is empty"
+ }
+ Write-Host "##vso[task.setvariable variable=$($environmentVariableName)]$($plainValue)"
+ }
+
+ $storageAccounts = Get-AzStorageAccount -ResourceGroupName $(ResourceGroupName)
+ $allStorageAccounts = ""
+ foreach ($storageAccount in $storageAccounts) {
+ $accKey = Get-AzStorageAccountKey -ResourceGroupName $(ResourceGroupName) -Name $storageAccount.StorageAccountName | Where-Object {$_.KeyName -eq "key1"}
+
+ $storageSecretName = "$($storageAccount.StorageAccountName)_secret"
+ Write-Host "##vso[task.setvariable variable=$($storageSecretName)]$($accKey.Value)"
+ $allStorageAccounts += "$($storageSecretName)|$($accKey.Value)|"
+ }
+ Write-Host "##vso[task.setvariable variable=AllStorageAccounts]$($allStorageAccounts)"
+
+ $appServiceName = "${{ parameters.appServiceName }}"
+ $appSettings = (Get-AzWebApp -ResourceGroupName $(ResourceGroupName) -Name $appServiceName).SiteConfig.AppSettings
+ $acrSettings = $appSettings | where {$_.Name -eq "FhirServer__Operations__ConvertData__ContainerRegistryServers__0"}
+ $acrLoginServer = $acrSettings[0].Value
+ $acrAccountName = ($acrLoginServer -split '\.')[0]
+ $acrPassword = (Get-AzContainerRegistryCredential -ResourceGroupName $(ResourceGroupName) -Name $acrAccountName).Password
+ Write-Host "##vso[task.setvariable variable=TestContainerRegistryServer]$($acrLoginServer)"
+ Write-Host "##vso[task.setvariable variable=TestContainerRegistryPassword]$($acrPassword)"
+
+ $exportStoreSettings = $appSettings | where {$_.Name -eq "FhirServer__Operations__Export__StorageAccountUri"}
+ $exportStoreUri = $exportStoreSettings[0].Value
+ Write-Host "$exportStoreUri"
+ $exportStoreAccountName = [System.Uri]::new("$exportStoreUri").Host.Split('.')[0]
+ $exportStoreKey = Get-AzStorageAccountKey -ResourceGroupName $(ResourceGroupName) -Name "$exportStoreAccountName" | Where-Object {$_.KeyName -eq "key1"}
+
+ Write-Host "##vso[task.setvariable variable=TestExportStoreUri]$($exportStoreUri)"
+ Write-Host "##vso[task.setvariable variable=TestExportStoreKey]$($exportStoreKey.Value)"
+
+ $integrationStoreSettings = $appSettings | where {$_.Name -eq "FhirServer__Operations__IntegrationDataStore__StorageAccountUri"}
+ $integrationStoreUri = $integrationStoreSettings[0].Value
+ Write-Host "$integrationStoreUri"
+ $integrationStoreAccountName = [System.Uri]::new("$integrationStoreUri").Host.Split('.')[0]
+ $integrationStoreKey = Get-AzStorageAccountKey -ResourceGroupName $(ResourceGroupName) -Name "$integrationStoreAccountName" | Where-Object {$_.KeyName -eq "key1"}
+
+ Write-Host "##vso[task.setvariable variable=TestIntegrationStoreUri]$($integrationStoreUri)"
+ Write-Host "##vso[task.setvariable variable=TestIntegrationStoreKey]$($integrationStoreKey.Value)"
+
+ Write-Host "##vso[task.setvariable variable=Resource]$(TestApplicationResource)"
+
+ $secrets = Get-AzKeyVaultSecret -VaultName resolute-oss-tenant-info
+
+ foreach($secret in $secrets)
+ {
+ $environmentVariableName = $secret.Name.Replace("--","_")
+
+ $secretValue = Get-AzKeyVaultSecret -VaultName resolute-oss-tenant-info -Name $secret.Name
+ # Replace with -AsPlainText flag when v5.3 of the Az Module is supported
+ $plainValue = ([System.Net.NetworkCredential]::new("", $secretValue.SecretValue).Password).ToString()
+ if([string]::IsNullOrEmpty($plainValue))
+ {
+ throw "$($secret.Name) is empty"
+ }
+ Write-Host "##vso[task.setvariable variable=$($environmentVariableName)]$($plainValue)"
+ }
+ # ----------------------------------------
+
+ dotnet dev-certs https
+
+ - task: DotNetCoreCLI@2
+ displayName: 'E2E ${{ parameters.version }} ${{parameters.appServiceType}}'
+ inputs:
+ command: test
+ arguments: '"$(Agent.TempDirectory)/E2ETests/**/*${{ parameters.version }}.Tests.E2E*.dll" --blame-hang-timeout 7m --filter "FullyQualifiedName~${{parameters.appServiceType}}&Category!=ExportLongRunning"'
+ workingDirectory: "$(System.ArtifactsDirectory)"
+ testRunTitle: '${{ parameters.version }} ${{parameters.appServiceType}}'
+ env:
+ 'TestEnvironmentUrl': $(TestEnvironmentUrl)
+ 'TestEnvironmentUrl_${{ parameters.version }}': $(TestEnvironmentUrl_${{ parameters.version }})
+ 'TestEnvironmentUrl_Sql': $(TestEnvironmentUrl_Sql)
+ 'TestEnvironmentUrl_${{ parameters.version }}_Sql': $(TestEnvironmentUrl_${{ parameters.version }}_Sql)
+ 'Resource': $(Resource)
+ 'AllStorageAccounts': $(AllStorageAccounts)
+ 'TestContainerRegistryServer': $(TestContainerRegistryServer)
+ 'TestContainerRegistryPassword': $(TestContainerRegistryPassword)
+ 'TestExportStoreUri': $(TestExportStoreUri)
+ 'TestExportStoreKey': $(TestExportStoreKey)
+ 'TestIntegrationStoreUri': $(TestIntegrationStoreUri)
+ 'TestIntegrationStoreKey': $(TestIntegrationStoreKey)
+ 'tenant-admin-service-principal-name': $(tenant-admin-service-principal-name)
+ 'tenant-admin-service-principal-password': $(tenant-admin-service-principal-password)
+ 'tenant-admin-user-name': $(tenant-admin-user-name)
+ 'tenant-admin-user-password': $(tenant-admin-user-password)
+ 'tenant-id': $(tenant-id)
+ 'app_globalAdminServicePrincipal_id': $(app_globalAdminServicePrincipal_id)
+ 'app_globalAdminServicePrincipal_secret': $(app_globalAdminServicePrincipal_secret)
+ 'app_nativeClient_id': $(app_nativeClient_id)
+ 'app_nativeClient_secret': $(app_nativeClient_secret)
+ 'app_wrongAudienceClient_id': $(app_wrongAudienceClient_id)
+ 'app_wrongAudienceClient_secret': $(app_wrongAudienceClient_secret)
+ 'user_globalAdminUser_id': $(user_globalAdminUser_id)
+ 'user_globalAdminUser_secret': $(user_globalAdminUser_secret)
+ 'user_globalConverterUser_id': $(user_globalConverterUser_id)
+ 'user_globalConverterUser_secret': $(user_globalConverterUser_secret)
+ 'user_globalExporterUser_id': $(user_globalExporterUser_id)
+ 'user_globalExporterUser_secret': $(user_globalExporterUser_secret)
+ 'user_globalImporterUser_id': $(user_globalImporterUser_id)
+ 'user_globalImporterUser_secret': $(user_globalImporterUser_secret)
+ 'user_globalReaderUser_id': $(user_globalReaderUser_id)
+ 'user_globalReaderUser_secret': $(user_globalReaderUser_secret)
+ 'user_globalWriterUser_id': $(user_globalWriterUser_id)
+ 'user_globalWriterUser_secret': $(user_globalWriterUser_secret)
diff --git a/.github/actions/package-integration-tests.yml b/.github/actions/package-integration-tests.yml
new file mode 100644
index 0000000000..180c259c07
--- /dev/null
+++ b/.github/actions/package-integration-tests.yml
@@ -0,0 +1,17 @@
+steps:
+
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet publish Integration Tests'
+ inputs:
+ command: publish
+ projects: 'test/**/*.csproj'
+ arguments: '--version-suffix $(build.buildNumber) -o "$(build.binariesdirectory)/IntegrationTests" --configuration $(buildConfiguration) --no-build -f $(defaultBuildFramework)'
+ publishWebProjects: false
+ zipAfterPublish: true
+
+ - task: PublishBuildArtifacts@1
+ displayName: 'publish Integration Tests'
+ inputs:
+ pathToPublish: '$(build.binariesdirectory)/IntegrationTests'
+ artifactName: 'IntegrationTests'
+ artifactType: 'container'
\ No newline at end of file
diff --git a/.github/actions/package-web-build-artifacts/action.yml b/.github/actions/package-web-build-artifacts/action.yml
new file mode 100644
index 0000000000..682417286f
--- /dev/null
+++ b/.github/actions/package-web-build-artifacts/action.yml
@@ -0,0 +1,27 @@
+name: Package Web Build Artifacts
+description: 'Packages the web build artifacts for deployment'
+inputs:
+ fhirSchemaVersion:
+ description: 'The FHIR schema version to package'
+ required: true
+ majorMinorPatch:
+ description: 'The version of the Nuget package'
+ required: true
+ outputPath:
+ description: 'The path to the output directory'
+ required: true
+ buildConfiguration:
+ description: 'The build configuration to use'
+ required: true
+ semVer:
+ description: 'The SemVer to use'
+ required: true
+runs:
+ using: 'composite'
+ steps:
+ - name: Publish Web Artifacts
+ shell: bash
+ run: |
+ echo "Publishing web artifacts for FHIR schema version ${{inputs.fhirSchemaVersion}}"
+ dotnet publish ${{github.workspace}}/src/Microsoft.Health.Fhir.${{inputs.fhirSchemaVersion}}.Web/Microsoft.Health.Fhir.${{inputs.fhirSchemaVersion}}.Web.csproj --output ${{inputs.outputPath}}/deploy/Microsoft.Health.Fhir.${{inputs.fhirSchemaVersion}}.Web --configuration ${{inputs.buildConfiguration}} --version-suffix ${{inputs.semVer}} --no-build -f ${{env.defaultDotNetVersion}}
+ zip Microsoft.Health.Fhir.${{inputs.fhirSchemaVersion}}.Web.zip ${{inputs.outputPath}}/deploy/Microsoft.Health.Fhir.${{inputs.fhirSchemaVersion}}.Web
diff --git a/.github/actions/package-web.yml b/.github/actions/package-web.yml
new file mode 100644
index 0000000000..edc1378081
--- /dev/null
+++ b/.github/actions/package-web.yml
@@ -0,0 +1,14 @@
+parameters:
+ csproj: '**/*Web.csproj'
+
+steps:
+
+ # Package web
+
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet publish ${{parameters.csproj}}'
+ inputs:
+ command: publish
+ projects: '${{parameters.csproj}}'
+ arguments: '--output $(build.artifactStagingDirectory)/web --configuration $(buildConfiguration) --version-suffix $(build.buildNumber) --no-build -f $(defaultBuildFramework)'
+ publishWebProjects: false
\ No newline at end of file
diff --git a/.github/actions/package.yml b/.github/actions/package.yml
new file mode 100644
index 0000000000..0fbf89aa9e
--- /dev/null
+++ b/.github/actions/package.yml
@@ -0,0 +1,93 @@
+steps:
+
+ # Package web
+ - template: package-web.yml
+ parameters:
+ csproj: '**/Microsoft.Health.Fhir.Stu3.Web.csproj'
+
+ - template: package-web.yml
+ parameters:
+ csproj: '**/Microsoft.Health.Fhir.R4.Web.csproj'
+
+ - template: package-web.yml
+ parameters:
+ csproj: '**/Microsoft.Health.Fhir.R4B.Web.csproj'
+
+ - template: package-web.yml
+ parameters:
+ csproj: '**/Microsoft.Health.Fhir.R5.Web.csproj'
+
+ # Package nugets
+ - powershell: |
+ & dotnet pack $(Build.SourcesDirectory) --output $(Build.ArtifactStagingDirectory)/nupkgs --no-build --configuration=Release -p:PackageVersion=$(nuGetVersion)
+ name: PackNugets
+
+ # Publish artifacts
+ - task: PublishBuildArtifacts@1
+ displayName: 'publish web artifacts'
+ inputs:
+ pathToPublish: '$(build.artifactStagingDirectory)/web'
+ artifactName: 'deploy'
+ artifactType: 'container'
+
+ - task: PublishBuildArtifacts@1
+ displayName: 'publish samples'
+ inputs:
+ pathToPublish: './samples/'
+ artifactName: 'deploy'
+ artifactType: 'container'
+
+ - task: PublishBuildArtifacts@1
+ displayName: 'publish testauthenvironment.json'
+ inputs:
+ pathToPublish: './testauthenvironment.json'
+ artifactName: 'deploy'
+ 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'
+ inputs:
+ pathToPublish: './test/Configuration/'
+ artifactName: 'deploy'
+ artifactType: 'container'
+
+ - task: PublishBuildArtifacts@1
+ displayName: 'publish release directory'
+ inputs:
+ pathToPublish: './release/'
+ artifactName: 'deploy'
+ artifactType: 'container'
+
+ - 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'
+
\ No newline at end of file
diff --git a/.github/actions/provision-healthcheck.yml b/.github/actions/provision-healthcheck.yml
new file mode 100644
index 0000000000..40107e6ebf
--- /dev/null
+++ b/.github/actions/provision-healthcheck.yml
@@ -0,0 +1,26 @@
+parameters:
+- name: webAppName
+ type: string
+
+steps:
+- powershell: |
+ $webAppName = "${{ parameters.webAppName }}".ToLower()
+ $healthCheckUrl = "https://$webAppName.azurewebsites.net/health/check"
+ $healthStatus = 0
+ Do {
+ Start-Sleep -s 5
+ Write-Host "Checking: $healthCheckUrl"
+
+ try {
+ $healthStatus = (Invoke-WebRequest -URI $healthCheckUrl).statuscode
+ Write-Host "Result: $healthStatus"
+ }
+ catch {
+ Write-Host $PSItem.Exception.Message
+ }
+ finally {
+ $Error.Clear()
+ }
+
+ } While ($healthStatus -ne 200)
+ name: PingHealthCheckEndpoint
diff --git a/.github/actions/provision-sqlServer.yml b/.github/actions/provision-sqlServer.yml
new file mode 100644
index 0000000000..190add675e
--- /dev/null
+++ b/.github/actions/provision-sqlServer.yml
@@ -0,0 +1,44 @@
+
+parameters:
+- name: resourceGroup
+ type: string
+- name: sqlServerName
+ type: string
+- name: schemaAutomaticUpdatesEnabled
+ type: string
+ default: 'auto'
+- name: sqlServerAdminPassword
+ type: string
+ default: ''
+
+jobs:
+- job: provisionEnvironment
+ pool:
+ name: '$(SharedLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - task: AzureKeyVault@1
+ displayName: 'Azure Key Vault: resolute-oss-tenant-info'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ KeyVaultName: 'resolute-oss-tenant-info'
+
+ - task: AzurePowerShell@5
+ displayName: 'Azure PowerShell script: InlineScript'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ azurePowerShellVersion: latestVersion
+ ScriptType: inlineScript
+ Inline: |
+ Add-Type -AssemblyName System.Web
+
+ $templateParameters = @{
+ sqlAdminPassword = "${{parameters.sqlServerAdminPassword}}"
+ sqlServerName = "${{parameters.sqlServerName}}".ToLower()
+ sqlSchemaAutomaticUpdatesEnabled = "${{parameters.schemaAutomaticUpdatesEnabled}}"
+ }
+
+ Write-Host "Provisioning Sql server"
+ Write-Host "Resource Group: ${{ parameters.resourceGroup }}"
+ Write-Host "SqlServerName: ${{ parameters.sqlServerName }}"
+ New-AzResourceGroupDeployment -ResourceGroupName "${{ parameters.resourceGroup }}" -TemplateFile $(System.DefaultWorkingDirectory)/samples/templates/default-sqlServer.json -TemplateParameterObject $templateParameters -Verbose
diff --git a/.github/actions/redeploy-webapp.yml b/.github/actions/redeploy-webapp.yml
new file mode 100644
index 0000000000..6845260147
--- /dev/null
+++ b/.github/actions/redeploy-webapp.yml
@@ -0,0 +1,29 @@
+parameters:
+- name: version
+ type: string
+- name: webAppName
+ type: string
+- name: subscription
+ type: string
+- name: imageTag
+ type: string
+
+jobs:
+- job: provisionEnvironment
+ pool:
+ name: '$(DefaultLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - task: AzureRmWebAppDeployment@4
+ displayName: 'Azure App Service Deploy'
+ inputs:
+ azureSubscription: '${{ parameters.subscription }}'
+ appType: 'webAppContainer'
+ WebAppName: '${{ parameters.webAppName }}'
+ DockerNamespace: $(azureContainerRegistry)
+ DockerRepository: '${{ parameters.version }}_fhir-server'
+ DockerImageTag: ${{ parameters.imageTag }}
+
+ - template: ./provision-healthcheck.yml
+ parameters:
+ webAppName: ${{ parameters.webAppName }}
\ No newline at end of file
diff --git a/.github/actions/run-tests.yml b/.github/actions/run-tests.yml
new file mode 100644
index 0000000000..d8672f3c1c
--- /dev/null
+++ b/.github/actions/run-tests.yml
@@ -0,0 +1,79 @@
+parameters:
+- name: version
+ type: string
+- name: keyVaultName
+ type: string
+- name: appServiceName
+ type: string
+jobs:
+- job: "integrationTests"
+ pool:
+ name: '$(SharedLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - task: DownloadBuildArtifacts@0
+ inputs:
+ buildType: 'current'
+ downloadType: 'single'
+ downloadPath: '$(System.ArtifactsDirectory)'
+ artifactName: 'IntegrationTests'
+
+ - task: ExtractFiles@1
+ displayName: 'Extract Integration Test Binaries'
+ inputs:
+ archiveFilePatterns: '$(System.ArtifactsDirectory)/IntegrationTests/Microsoft.Health.Fhir.${{ parameters.version }}.Tests.Integration.zip'
+ destinationFolder: '$(Agent.TempDirectory)/IntegrationTests/'
+
+ - task: UseDotNet@2
+ inputs:
+ useGlobalJson: true
+
+ - task: AzureKeyVault@1
+ displayName: 'Azure Key Vault: ${{ parameters.keyVaultName }}'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ KeyVaultName: '${{ parameters.keyVaultName }}'
+
+ - task: AzureKeyVault@1
+ displayName: 'Azure Key Vault: ${{ parameters.keyVaultName }}-sql'
+ inputs:
+ azureSubscription: $(ConnectedServiceName)
+ KeyVaultName: '${{ parameters.keyVaultName }}-sql'
+
+ - task: DotNetCoreCLI@2
+ displayName: 'Run Integration Tests'
+ inputs:
+ command: test
+ arguments: '"$(Agent.TempDirectory)/IntegrationTests/**/*${{ parameters.version }}.Tests.Integration*.dll" --blame-hang-timeout 15m'
+ workingDirectory: "$(System.ArtifactsDirectory)"
+ testRunTitle: '${{ parameters.version }} Integration'
+ env:
+ 'CosmosDb:Host': $(CosmosDb--Host)
+ 'CosmosDb:Key': $(CosmosDb--Key)
+ 'SqlServer:ConnectionString': $(SqlServer--ConnectionString)
+
+- job: 'cosmosE2eTests'
+ dependsOn: []
+ pool:
+ name: '$(SharedLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - template: e2e-setup.yml
+ - template: e2e-tests.yml
+ parameters:
+ version: ${{ parameters.version }}
+ appServiceName: ${{ parameters.appServiceName }}
+ appServiceType: 'CosmosDb'
+
+- job: 'sqlE2eTests'
+ dependsOn: []
+ pool:
+ name: '$(SharedLinuxPool)'
+ vmImage: '$(LinuxVmImage)'
+ steps:
+ - template: e2e-setup.yml
+ - template: e2e-tests.yml
+ parameters:
+ version: ${{ parameters.version }}
+ appServiceName: '${{ parameters.appServiceName }}-sql'
+ appServiceType: 'SqlServer'
diff --git a/.github/actions/setup-build-variables/action.yml b/.github/actions/setup-build-variables/action.yml
new file mode 100644
index 0000000000..b78873f84b
--- /dev/null
+++ b/.github/actions/setup-build-variables/action.yml
@@ -0,0 +1,38 @@
+name: setup build variables
+description: Sets variables used during builds.
+
+runs:
+ using: composite
+ steps:
+ - name: Set Build Variables
+ id: defaultVariables
+ shell: bash
+ run: |
+ echo "buildConfiguration=Release" >> "$GITHUB_ENV"
+ echo "defaultBuildFramework=net8.0" >> "$GITHUB_ENV"
+ echo "azureSubscriptionEndpoint=docker-build" >> "$GITHUB_ENV"
+ echo "azureContainerRegistryName=healthplatformregistry" >> "$GITHUB_ENV"
+ echo "connectedServiceName=Microsoft Health Open Source Subscription" >> "$GITHUB_ENV"
+ echo "composeLocation=build/docker/docker-compose.yaml" >> "$GITHUB_ENV"
+
+ - name: Set Build Urls using Deployment Environment
+ shell: bash
+ run: |
+ echo "azureContainerRegistry='$azureContainerRegistryName'.azurecr.io" >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameSql='$deploymentEnvironmentName-sql' >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameR4='$deploymentEnvironmentName-r4' >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameR4Sql='$deploymentEnvironmentNameR4'-sql >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameR4B='$deploymentEnvironmentName-r4b' >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameR4BSql='$deploymentEnvironmentNameR4B'-sql >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameR5='$deploymentEnvironmentName'-r5 >> "$GITHUB_ENV"
+ echo "deploymentEnvironmentNameR5Sql='$deploymentEnvironmentNameR5'-sql >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl=https://'$deploymentEnvironmentName'.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_Sql=https://'$deploymentEnvironmentName'-sql.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_R4=https://'$deploymentEnvironmentName'-r4.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_R4_Sql=https://'$deploymentEnvironmentName'-r4-sql.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_R4B=https://'$deploymentEnvironmentName'-r4b.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_R4B_Sql=https://'$deploymentEnvironmentName'-r4b-sql.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_R5=https://'$deploymentEnvironmentName'-r5.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testEnvironmentUrl_R5_Sql=https://'$deploymentEnvironmentName'-r5-sql.azurewebsites.net >> "$GITHUB_ENV"
+ echo "testClientUrl=https://'$deploymentEnvironmentName'-client/ >> "$GITHUB_ENV"
+ echo "testApplicationResource=https://'$deploymentEnvironmentName'.'$tenantDomain' >> "$GITHUB_ENV"
diff --git a/.github/actions/update-semver/action.yml b/.github/actions/update-semver/action.yml
new file mode 100644
index 0000000000..ccaa23c812
--- /dev/null
+++ b/.github/actions/update-semver/action.yml
@@ -0,0 +1,39 @@
+name: update-semver
+description: 'Update the build number with the SemVer from GitVersion'
+inputs:
+ configFilePath:
+ description: 'Path to the GitVersion configuration file'
+ required: false
+ default: './GitVersion.yml'
+outputs:
+ assemblyVersion:
+ description: The assembly version for the build
+ value: ${{ steps.version.outputs.GitVersion_AssemblySemVer }}
+ fileVersion:
+ description: The assembly file version for the build
+ value: ${{ steps.version.outputs.GitVersion_AssemblySemFileVer }}
+ informationalVersion:
+ description: The assembly informational version for the build
+ value: ${{ steps.version.outputs.GitVersion_InformationalVersion }}
+ semVer:
+ description: The NuGet package version for the build
+ value: ${{ steps.version.outputs.GitVersion_SemVer }}
+ majorMinorPatch:
+ description: The major.minor.patch version for the build
+ value: ${{ steps.version.outputs.GitVersion_MajorMinorPatch }}
+runs:
+ using: 'composite'
+ steps:
+
+ - name: Install GitVersion'
+ uses: gittools/actions/gitversion/setup@v0.13.4
+ with:
+ versionSpec: '5.x'
+
+ - name: SetVariablesFromGitVersion
+ id: version
+ uses: gittools/actions/gitversion/execute@v0.13.4
+ with:
+ configFilePath: ${{inputs.configFilePath}}
+ targetPath: ${{github.workspace}}
+ useConfigFile: true
diff --git a/.github/actions/update-sqlAdminPassword.yml b/.github/actions/update-sqlAdminPassword.yml
new file mode 100644
index 0000000000..b1d4a2466e
--- /dev/null
+++ b/.github/actions/update-sqlAdminPassword.yml
@@ -0,0 +1,18 @@
+steps:
+
+- task: UseDotNet@2
+ displayName: 'Use .NET Core sdk (to generate password)'
+ inputs:
+ packageType: sdk
+ version: 3.1.x
+
+- task: UseDotNet@2
+ inputs:
+ useGlobalJson: true
+
+- powershell: |
+
+ $random = -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=password;isOutput=true]"
+
+ name: SetVariablesFromRandomString
diff --git a/.github/workflows/fhir-oss-ci-pipeline.yml b/.github/workflows/fhir-oss-ci-pipeline.yml
new file mode 100644
index 0000000000..30e3542060
--- /dev/null
+++ b/.github/workflows/fhir-oss-ci-pipeline.yml
@@ -0,0 +1,261 @@
+# DESCRIPTION:
+# Builds, tests, and packages the solution for the main branch.
+
+on:
+ pull_request
+
+permissions:
+ id-token: write
+ contents: read
+
+defaults:
+ run:
+ working-directory: src
+ shell: bash
+
+env:
+ buildConfiguration: Release
+ azureSubscriptionEndpoint: docker-build
+ azureContainerRegistryName: healthplatformregistry
+ connectedServiceName: Microsoft Health Open Source Subscription
+ composeLocation: build/docker/docker-compose.yaml
+ imageTag: ${{github.run_number}}
+ outputPath: ${{github.workspace}}/artifacts
+ defaultDotNetVersion: net8.0
+
+jobs:
+ setup:
+ runs-on: [self-hosted, 1ES.Pool=GithubRunPool]
+ env:
+ deploymentEnvironmentName: $vars.CIRESOURCEGROUPROOT
+ appServicePlanName: $vars.CIRESOURCEGROUPROOT-linux
+ resourceGroupName: $vars.CIRESOURCEGROUPROOT
+ outputs:
+ assemblyVersion: ${{ steps.version.outputs.assemblyVersion }}
+ fileVersion: ${{ steps.version.outputs.fileVersion }}
+ informationalVersion: ${{ steps.version.outputs.informationalVersion }}
+ majorMinorPatch: ${{ steps.version.outputs.majorMinorPatch }}
+ semVer: ${{steps.version.outputs.SemVer}}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Install Latest .Net SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ global-json-file: 'global.json'
+ dotnet-version: |
+ 8.x
+
+ - name: Determine Semver
+ id: version
+ uses: ./.github/actions/update-semver
+
+ # - name: Azure Login
+ # uses: azure/login@v2
+ # with:
+ # client-id: ${{secrets.AZURE_CLIENT_ID}}
+ # subscription-id: ${{secrets.AZURE_SUBSCRIPTION_ID}}
+ # tenant-id: ${{secrets.AZURE_TENANT_ID}}
+ # enable-AzPSSession: true
+
+ # - name: Clean Storage Accounts
+ # uses: ./.github/actions/clean-storage-accounts
+ # with:
+ # environmentName: ${{vars.CIRESOURCEGROUPROOT}}
+ # - name: Cleanup Integration Test databases
+ # uses: ./.github/actions/cleanup-integration-test-databases
+ # with:
+ # environmentName: ${{vars.CIRESOURCEGROUPROOT}}
+ buildAndUnitTest:
+ runs-on: windows-latest
+ needs: setup
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check DotNet Version
+ run: dotnet --version
+
+ - name: Build
+ uses: ./.github/actions/dotnet-build
+ with:
+ assemblyVersion: ${{needs.setup.outputs.assemblyVersion}}
+ buildConfiguration: ${{env.buildConfiguration}}
+ fileVersion: ${{needs.setup.outputs.fileVersion}}
+ informationalVersion: ${{needs.setup.outputs.informationalVersion}}
+ majorMinorPatch: ${{needs.setup.outputs.majorMinorPatch}}
+ - name: TestCore
+ uses: ./.github/actions/dotnet-test-core
+ with:
+ buildConfiguration: ${{env.buildConfiguration}}
+ - name: TestWeb
+ uses: ./.github/actions/dotnet-test-web
+ with:
+ buildConfiguration: ${{env.buildConfiguration}}
+
+ # - name: Generate SBOM
+ # run: |
+ # curl -Lo $RUNNER_TEMP/sbom-tool https://github.com/microsoft/sbom-tool/releases/latest/download/sbom-tool-linux-x64
+ # chmod +x $RUNNER_TEMP/sbom-tool
+ # $RUNNER_TEMP/sbom-tool generate -b ${{env.outputPath}} -bc . -V Verbose -ps "Organization: Microsoft" -pv ${{needs.setup.outputs.majorMinorPatch}} -pn ${{needs.setup.outputs.informationalVersion}}
+
+ # - name: Upload a Build Artifact
+ # uses: actions/upload-artifact@v4
+ # with:
+ # name: build
+ # path: ${{env.outputPath}}
+ - name: Create Nuget packages
+ shell: bash
+ run: |
+ echo "Creating Nuget packages"
+ dotnet pack ${{github.workspace}}\Microsoft.Health.Fhir.sln --output ${{env.outputPath}}/nupkgs --no-build --configuration=${{env.buildConfiguration}} -p:PackageVersion=${{needs.setup.outputs.majorMinorPatch}}
+
+ - name: Upload Nuget Packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget
+ path: ${{env.outputPath}}/nupkgs
+
+ - name: samples
+ shell: bash
+ run: |
+ echo "Copying samples to deploy directory"
+ cp -r ${{github.workspace}}/samples ${{env.outputPath}}/deploy
+
+ - name: Copying testauthenvironment.json to deploy directory
+ shell: bash
+ run: |
+ echo "Copying testauthenvironment.json to deploy directory"
+ cp ${{github.workspace}}/testauthenvironment.json ${{env.outputPath}}/deploy/
+
+ - name: Copying global.json to deploy directory
+ shell: bash
+ run: |
+ echo "Copying global.json to deploy directory"
+ cp ${{github.workspace}}/global.json ${{env.outputPath}}/deploy/
+
+ - name: Copying test configuration json to deploy directory
+ shell: bash
+ run: |
+ echo "Copying test configuration json to deploy directory"
+ cp ${{github.workspace}}/test/Configuration/testconfiguration.json ${{env.outputPath}}/deploy/
+
+ - name: Copying docker compose root file to deploy directory
+ shell: bash
+ run: |
+ echo "Copying docker compose root file to deploy directory"
+ cp ${{github.workspace}}/release/docker-compose.yaml ${{env.outputPath}}/deploy
+
+ - name: Copying pdb files to symbols directory
+ shell: bash
+ run: |
+ echo "Copying pdb files to deploy symbols"
+ find ${{github.workspace}}/src -type f -name "*.pdb" ! -name "*UnitTest*" -exec cp {} ${{env.outputPath}}/symbols \;
+
+ - name: Publish Stu3 Web Artifacts to deploy directory
+ uses: ./.github/actions/package-web-build-artifacts
+ with:
+ fhirschemaversion: "Stu3"
+ majorMinorPatch: ${{needs.setup.outputs.majorMinorPatch}}
+ outputPath: ${{env.outputPath}}
+ buildConfiguration: ${{env.buildConfiguration}}
+ semVer: ${{needs.setup.outputs.semVer}}
+
+ - name: Publish R4 Web Artifacts to deploy directory
+ uses: ./.github/actions/package-web-build-artifacts
+ with:
+ fhirschemaversion: "R4"
+ majorMinorPatch: ${{needs.setup.outputs.majorMinorPatch}}
+ outputPath: ${{env.outputPath}}
+ buildConfiguration: ${{env.buildConfiguration}}
+ semVer: ${{needs.setup.outputs.semVer}}
+
+ - name: Publish R4B Web Artifacts to deploy directory
+ uses: ./.github/actions/package-web-build-artifacts
+ with:
+ fhirschemaversion: "R4B"
+ majorMinorPatch: ${{needs.setup.outputs.majorMinorPatch}}
+ outputPath: ${{env.outputPath}}
+ buildConfiguration: ${{env.buildConfiguration}}
+ semVer: ${{needs.setup.outputs.semVer}}
+
+ - name: Publish R5 Web Artifacts to deploy directory
+ uses: ./.github/actions/package-web-build-artifacts
+ with:
+ fhirschemaversion: "R5"
+ majorMinorPatch: ${{needs.setup.outputs.majorMinorPatch}}
+ outputPath: ${{env.outputPath}}
+ buildConfiguration: ${{env.buildConfiguration}}
+ semVer: ${{needs.setup.outputs.semVer}}
+
+ - name: Docker Build Stu3 Image
+ uses: ./.github/actions/docker-build
+ with:
+ assemblyVersion: ${{needs.setup.outputs.majorMinorPatch}}
+ fhirSchemaVersion: "Stu3"
+ composeLocation: ${{env.composeLocation}}
+
+ - name: Docker Build R4 Image
+ uses: ./.github/actions/docker-build
+ with:
+ assemblyVersion: ${{needs.setup.outputs.majorMinorPatch}}
+ fhirSchemaVersion: "R4"
+ composeLocation: ${{env.composeLocation}}
+
+ - name: Docker Build R4B Image
+ uses: ./.github/actions/docker-build
+ with:
+ assemblyVersion: ${{needs.setup.outputs.majorMinorPatch}}
+ fhirSchemaVersion: "R4B"
+ composeLocation: ${{env.composeLocation}}
+
+ - name: Docker Build R5 Image
+ uses: ./.github/actions/docker-build
+ with:
+ assemblyVersion: ${{needs.setup.outputs.majorMinorPatch}}
+ fhirSchemaVersion: "R5"
+ composeLocation: ${{env.composeLocation}}
+
+ - name: Upload deploy directory
+ uses: actions/upload-artifact@v4
+ with:
+ name: deploy
+ path: ${{env.outputPath}}/deploy
+
+ # - name: Upload Symbols
+ # uses: actions/upload-artifact@v4
+ # with:
+ # name: symbols
+ # path: ${{env.outputPath}}/bin/${{env.buildConfiguration}}/net5.0/publish
+
+ # runIntegrationTests:
+ # runs-on: [self-hosted, 1ES.Pool=GithubRunPool, Windows]
+ # needs : buildAndUnitTest
+ # steps:
+ # - name: Checkout
+ # uses: actions/checkout@v4
+ # with:
+ # fetch-depth: 0
+ # - name: Download Build Artifact for Testing
+ # uses: actions/download-artifact@v4
+ # with:
+ # path: artifacts
+ # - name: Install Latest .Net SDK
+ # uses: actions/setup-dotnet@v4
+ # with:
+ # global-json-file: 'global.json'
+ # dotnet-version: |
+ # 6.x
+ # 8.x
+ # - name: Docker add main tag
+ # uses: ./.github/actions/docker-add-main-tag
+ # with:
+ # assemblySemFileVer: ${{needs.setup.outputs.semVer}}
+ # imageTag: ${{env.imageTag}}
+ # azureContainerRegistryName: ${{env.azureContainerRegistryName}}
+ # connectedServiceName: ${{env.connectedServiceName}}
diff --git a/.github/workflows/simple-build.yml b/.github/workflows/simple-build.yml
new file mode 100644
index 0000000000..8be0c7b84c
--- /dev/null
+++ b/.github/workflows/simple-build.yml
@@ -0,0 +1,62 @@
+# This workflow will build a .NET project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
+
+on:
+ pull_request
+
+env:
+ buildConfiguration: Release
+ azureSubscriptionEndpoint: docker-build
+ azureContainerRegistryName: healthplatformregistry
+ connectedServiceName: Microsoft Health Open Source Subscription
+ composeLocation: build/docker/docker-compose.yaml
+ imageTag: ${{github.run_number}}
+ outputPath: ${{github.workspace}}/artifacts
+
+jobs:
+ buildTestAndPackage:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.0.204
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build
+ shell: bash
+ run: dotnet build "./Microsoft.Health.Fhir.sln" --no-restore
+ # run: dotnet build "./Microsoft.Health.Fhir.sln" --configuration ${{inputs.buildConfiguration}} "-p:ContinuousIntegrationBuild=true;AssemblyVersion=${{inputs.assemblyVersion}};FileVersion=${{inputs.fileVersion}};InformationalVersion=${{inputs.informationalVersion}};Version=${{inputs.majorMinorPatch}}" -warnaserror
+
+ # - name: Test
+ # run: dotnet test "Microsoft.Health.Fhir.sln" --no-build --filter "FullyQualifiedName~UnitTests" --verbosity normal
+ # # run: dotnet test "Microsoft.Health.Fhir.sln" -p:ContinuousIntegrationBuild=true; --filter "FullyQualifiedName~Core.UnitTests" --configuration ${{inputs.buildConfiguration}} --no-build --verbosity normal
+
+ - name: Create Nuget packages
+ run: |
+ echo "Creating Nuget packages"
+ dotnet pack ${{github.workspace}}\Microsoft.Health.Fhir.sln --output ${{env.outputPath}}/nupkgs --no-build -c Release
+ # dotnet pack ${{github.workspace}}\Microsoft.Health.Fhir.sln --output ${{env.outputPath}}/nupkgs --no-build --configuration=${{env.buildConfiguration}} -p:PackageVersion=${{needs.setup.outputs.majorMinorPatch}}
+
+ - name: Upload Nuget Packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget
+ path: ${{env.outputPath}}/nupkgs
+
+ - name: Login to Azure
+ uses: azure/login@v1
+ with:
+ creds: ${{ secrets.AZURE_CREDENTIALS }}
+ #what is the compliant way to use secrets in github actions, look at
+ # https://docs.opensource.microsoft.com/
+
+ - name: Build Docker image
+ run: |
+ docker build -t myregistry.azurecr.io/myimage:${{ github.sha }} .
+ docker push myregistry.azurecr.io/myimage:${{ github.sha }}
diff --git a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportOrchestratorJobTests.cs b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportOrchestratorJobTests.cs
index 7a964492f9..9c18ff502d 100644
--- a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportOrchestratorJobTests.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportOrchestratorJobTests.cs
@@ -32,6 +32,7 @@
namespace Microsoft.Health.Fhir.SqlServer.UnitTests.Features.Operations.Import
{
+ [Collection("Sequential")]
[Trait(Traits.OwningTeam, OwningTeam.FhirImport)]
[Trait(Traits.Category, Categories.Import)]
public class ImportOrchestratorJobTests
diff --git a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportProcessingJobTests.cs b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportProcessingJobTests.cs
index a325118c65..3070e3821a 100644
--- a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportProcessingJobTests.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Operations/Import/ImportProcessingJobTests.cs
@@ -29,6 +29,7 @@
namespace Microsoft.Health.Fhir.SqlServer.UnitTests.Features.Operations.Import
{
+ [Collection("Sequential")]
[Trait(Traits.OwningTeam, OwningTeam.FhirImport)]
[Trait(Traits.Category, Categories.Import)]
public class ImportProcessingJobTests
diff --git a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/CustomQueriesUnitTests.cs b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/CustomQueriesUnitTests.cs
index 736493dd7b..09c328ae80 100644
--- a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/CustomQueriesUnitTests.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/CustomQueriesUnitTests.cs
@@ -14,6 +14,7 @@
namespace Microsoft.Health.Fhir.SqlServer.UnitTests.Features.Search
{
+ [Collection("Sequential")]
[Trait(Traits.OwningTeam, OwningTeam.Fhir)]
[Trait(Traits.Category, Categories.Search)]
public class CustomQueriesUnitTests
diff --git a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/FlatteningRewriterTests.cs b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/FlatteningRewriterTests.cs
index da35abbc56..568095f336 100644
--- a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/FlatteningRewriterTests.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/FlatteningRewriterTests.cs
@@ -11,6 +11,7 @@
namespace Microsoft.Health.Fhir.SqlServer.UnitTests.Features.Search.Expressions
{
+ [Collection("Sequential")]
[Trait(Traits.OwningTeam, OwningTeam.Fhir)]
[Trait(Traits.Category, Categories.Search)]
public class FlatteningRewriterTests
diff --git a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/LastUpdatedToResourceSurrogateIdRewriterTests.cs b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/LastUpdatedToResourceSurrogateIdRewriterTests.cs
index 058edaaac3..578f8fcecf 100644
--- a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/LastUpdatedToResourceSurrogateIdRewriterTests.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/LastUpdatedToResourceSurrogateIdRewriterTests.cs
@@ -14,6 +14,7 @@
namespace Microsoft.Health.Fhir.SqlServer.UnitTests.Features.Search.Expressions
{
+ [Collection("Sequential")]
[Trait(Traits.OwningTeam, OwningTeam.Fhir)]
[Trait(Traits.Category, Categories.Search)]
public class LastUpdatedToResourceSurrogateIdRewriterTests
diff --git a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/SqlServerSortingValidatorTests.cs b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/SqlServerSortingValidatorTests.cs
index 9cabbd508a..3d5f664f87 100644
--- a/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/SqlServerSortingValidatorTests.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer.UnitTests/Features/Search/Expressions/SqlServerSortingValidatorTests.cs
@@ -17,6 +17,7 @@
namespace Microsoft.Health.Fhir.SqlServer.UnitTests.Features.Search.Expressions
{
+ [Collection("Sequential")]
[Trait(Traits.OwningTeam, OwningTeam.Fhir)]
[Trait(Traits.Category, Categories.Search)]
public class SqlServerSortingValidatorTests
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj b/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj
index b1a6e7b04b..4b44b5f9ad 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj
+++ b/src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj
@@ -10,10 +10,10 @@
-
+
-
+
diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs
index de22234693..9f7631c945 100644
--- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs
+++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs
@@ -16,7 +16,6 @@
using Microsoft.Health.Fhir.Tests.Common;
using Microsoft.Health.Fhir.Tests.Common.FixtureParameters;
using Microsoft.Health.Test.Utilities;
-using Microsoft.SqlServer.Management.Sdk.Sfc;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;